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)
您可能更喜欢通过
延迟不同步执行
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 处理程序。
第二个选项需要制作任何所需数据的 "snapshot" 副本并将其与延迟调用请求一起传递(因为 VCL 更新代码可能在 HTTP 之后(或期间)的任何随机时间执行处理程序(或多个 HTTP 处理程序)。当多个 HTTP 处理程序请求 GUI 更新并且您必须检查其中哪些传递了最新数据并跳过其他请求时,这是正常的。
想知道是否有人可以帮助解决一个非常棘手的零星(不能始终重现)问题 - 可能与线程有关。我在 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)
您可能更喜欢通过
延迟不同步执行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 处理程序。
第二个选项需要制作任何所需数据的 "snapshot" 副本并将其与延迟调用请求一起传递(因为 VCL 更新代码可能在 HTTP 之后(或期间)的任何随机时间执行处理程序(或多个 HTTP 处理程序)。当多个 HTTP 处理程序请求 GUI 更新并且您必须检查其中哪些传递了最新数据并跳过其他请求时,这是正常的。