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

Delphi 7 Occasional deadlock changing TLabel.Font.Style from IdHTTPListener event

想知道是否有人可以帮助解决一个非常棘手的零星(不能始终重现)问题 - 可能与线程有关。我在 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(s) 会做这样的事情:-

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,您使用网络请求选择的 TLabel 会变成粗体。

问题:有时,不可预料地,我在处理 Web 请求时遇到某种死锁 - 它只是冻结,带有圆形漩涡状鼠标指针,并且无法恢复。如果我在 Delphi 中调试它,调用堆栈是空的,我只能单步执行汇编代码 - 恐怕我不明白这意味着什么!我通过在每一行代码之间编写一行文本文件,将其追踪到上面的 Pagelabs[j].Font.Style:=[fsBold] 行......所以虽然错误是零星的,但它确实发生了, 它总是锁定在那条线上。

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

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

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

您有两个选择:

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

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

您可能更喜欢通过

延迟不同步执行

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

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