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

时间:2015-12-06 16:29:50

标签: delphi user-interface thread-safety deadlock indy

想知道是否有人可以提供一个非常棘手的零星(不一致可重复)问题 - 可能与线程有关。我在Delphi 7(它的旧代码......)中运行了一个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变为粗体。

问题:有时候,不可预测的是,我遇到了处理网页请求的某种僵局 - 它只是用圆形的旋转鼠标指针冻结,并且无法恢复。如果我在Delphi中调试它,那么调用堆栈是空的,我只能单步执行汇编代码 - 我担心我不能正常工作!我通过在每一行代码之间写一行文本文件来跟踪上面的Pagelabs [j] .Font.Style:= [fsBold]行...所以虽然错误是零星的,当它确实发生时,它总是被它锁定的那条线。

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

任何想法都非常感激, 谢谢, 韦斯

1 个答案:

答案 0 :(得分:4)

您无法仅从主GUI线程操纵工作线程的UI控件!

您有两种选择:

您可以通过TThread.Synchronize()过程阻止HTTP线程并临时切换到主线程。就像在这个例子中一样:

http://docwiki.embarcadero.com/CodeExamples/Seattle/en/Synchronize_(Delphi)

您可能更喜欢通过

延迟不同步执行

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

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