Delphi 7 IdHTTPListener事件中的TLabel.Font.Style偶尔发生死锁更改



想知道是否有人能帮助解决一个非常棘手的偶发(不可重复)问题——可能与线程有关。我在Delphi7中(它是旧代码…),运行一个IdHttpListener(Indy)。下面的代码是从一个笨重的大型应用程序中复制的,但希望足以解释。传入的HTTP请求运行以下事件,其中web_lock是我在顶部定义的TCriticalSection。我在关键部分做了这件事,因为web请求会导致我需要的原子性更改。

procedure TFWebServer.WebServerCommandGet(Thread: TIdPeerThread;
  RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo);
var S,PageString : string;
begin
  web_lock.Acquire;
  PageString:=' ';
  if (copy(RequestInfo.Document,1,15)='/_Request_Part_') then begin
    s:=copy(RequestInfo.Document,16,length(RequestInfo.Document));
    FMainGui.doFunction(s);
    PageString:='OK';
  end; // Lots more else cases here...
  ResponseInfo.ContentType:='text/plain';
  ResponseInfo.ResponseNo:=200;
  ResponseInfo.ContentStream:=TMemoryStream.Create;
  ResponseInfo.ContentStream.Write(PageString[1],length(PageString)*
    sizeof(PageString[1]));
  ResponseInfo.ContentLength:=length(PageString)*sizeof(PageString[1]);
  web_lock.Release;
end;

然后,我的FMainGui.doFunction做了这样的事情:-

procedure doFunction(s : String);
var i,j : integer;
begin
  i:=strtoint(s); 
  for j:=1 to Pages do begin // Pages is dynamic - but correctly set
    if (j=i) then // Pagelabs[j] is always a visible TLabel
      Pagelabs[j].Font.Style:=[fsBold]
      else Pagelabs[j].Font.Style:=[];
  end;
end;

稍微简化了一点——Pagelabs是一组动态创建的TLabel,我会在页面上显示,而您使用web请求选择的那个TLabel会变为粗体。

问题:偶尔,也不可预测地,我会在处理web请求时遇到某种死锁——它只是冻结了,带有圆形漩涡鼠标指针,无法恢复。如果我在Delphi中调试它,调用堆栈是空的,我只能逐步完成汇编代码——恐怕我无法完成这意味着什么!我追踪到上面的Pagelabs[j].Font.Style:=[fsBold]行,在每一行代码之间写一行文本文件。。。因此,虽然错误是偶发的,但当它确实发生时,它总是锁定在那条线上。

我很感激这是一个大型应用程序的片段,但有什么明显的我做错了吗?例如,从HTTP侦听器触发的线程更改GUI属性是否安全?或者我应该做一些不同的事情?

任何想法都值得赞赏,谢谢Wes

您不能从辅助线程操作UI控件,只能从主GUI线程操作!

你有两个选择:

您可以通过TThread.Synchronize()过程阻塞HTTP线程并临时切换到主线程。如本例所示:

http://docwiki.embarcadero.com/CodeExamples/Seattle/en/Synchronize_(德尔福)

您可能更喜欢通过延迟不同步执行

  • Windows邮件(使用PostMessage,避免使用SendMessage)http://www.cryer.co.uk/brian/delphi/howto_send_custom_window_message.htm

  • AsyncCall库http://andy.jgknet.de/blog/bugfix-units/asynccalls-29-asynchronous-function-calls/

  • 类似OmniThreadsLibrary 的线程队列方法

第一个选项可能会减慢速度,因为GUI线程中的任何长时间处理都会冻结HTTP处理程序。

第二个选项需要制作任何所需数据的"快照"副本,并将其与延迟调用请求一起传递(因为VCL更新代码可能在HTTP处理程序(或几个HTTP处理程序)之后(或期间)的任何随机时间执行)。当几个HTTP处理程序请求GUI更新时,这是正常的,例如,您必须检查其中哪些处理程序传递了最新的数据并跳过其他请求。

最新更新