Delphi 7 Occasional deadlock changing TLabel.Font.

2020-05-03 10:48发布

问题:

Wondered if anyone could help with a really tricky sporadic (not consistently reproducible) issue - possibly to do with threads. I'm in Delphi 7 (it's old code...), running an IdHttpListener (Indy). Code below is copied from a large cumbersome app - but hopefully enough to explain. An incoming HTTP request runs the following event - where web_lock is a TCriticalSection I define at the top. I do it in a critical section as the web requests causes changes that I need to be atomic.

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;

and then, my FMainGui.doFunction(s) does something like this:-

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;

Bit simplified - Pagelabs is a set of dynamically created TLabels that I display on a page, and the one you've selected using the web request gets made bold.

THE PROBLEM: Occasionally, and unpredictably, I get some kind of deadlock dealing with the web request - it just freezes, with circular swirly mouse pointer, and doesn't recover. If I'm debugging it in Delphi, the call stack is empty, and I can only step through the assembly code - afraid I'm not up to working what that means! I tracked it down to the Pagelabs[j].Font.Style:=[fsBold] line above, by writing one line text files between every single line of code... so while the error was sporadic, when it did occur, it was always that line it locked up on.

I appreciate this is a snippet of a large app, but is there anything obvious I'm doing wrong? Eg - should it be safe to change GUI properties from a thread triggered by the HTTP Listener? Or is there something different I should be doing?

Any ideas much appreciated, Thanks, Wes

回答1:

You cannot manipulate UI controls from worker threads, only from the main GUI thread!

You have two options:

You may block your HTTP threads and temporary switch to main thread by TThread.Synchronize() procedure. Like in this example:

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

You may prefer delayed out-of-sync execution via

  • Windows messages ( use PostMessage and avoid SendMessage )http://www.cryer.co.uk/brian/delphi/howto_send_custom_window_message.htm

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

  • thread-queues approach with something like OmniThreadsLibrary

First option might slow you down, for any long processing in the GUI thread would also freeze your HTTP handlers.

Second options would require making a "snapshot" copy of any required data and passing it along with the deferred call request ( as the VCL update code might be executed at any random time AFTER (or during) the HTTP handler (or several HTTP handlers). It would be normal when several HTTP handlers would request GUI updates and you would have to check which of them passed the most recent data and skip other requests for example.