Application.ProcessMessages hangs?
my single threaded delphi 2009 app (not quite yet complete) has started to have a problem with Application.ProcessMessages hanging. my app has a TTimer object that fires every 100 ms to poll an external device. i use Application.ProcessMessages to update the screen when something changes so the app is still responsive.
one of these was in a grid OnMouseDown event. in there, it had an Application.ProcessMessages that essentially hung. removing that was no problem except that i soon discovered another Application.ProcessMe开发者_运维技巧ssages that was also blocking.
i think what may be happening to me is that the TTimer is--in the app mode i'm currently debugging--probably taking too long to complete. i have prevented the TTimer.OnTimer event hander from re-entering the same code (see below):
procedure TfrmMeas.tmrCheckTimer(Sender: TObject);
begin
if m_CheckTimerBusy then
exit;
m_CheckTimerBusy:=true;
try
PollForAndShowMeasurements;
finally
m_CheckTimerBusy:=false;
end;
end;
what places would it be a bad practice to call Application.ProcessMessages? OnPaint routines springs to mind as something that wouldn't make sense.
any general recommendations?
i am surprised to see this kind of problem arise at this point in the development!
My recommendation regarding TApplication.ProcessMessages
is to never ever use it - there simply isn't a good point to place it.
Imagine what calling it does: your application runs a message loop - where the windows messages (produced by OS, other apps, your app etc) are sequentially processed - and there, in the middle of one of the message-processings, you just re-run the whole message loop again, without having control over what messages will be processed, how much of them there will be, if any of the messages will enter its' own message loop... and if they have any reentrancy problems or not. That's what I call inviting trouble.
There are sometimes good reasons to process some windows messages (particularry to not hang other threads), or to process all messagess directed to a particular window, but this may be accomplished in more subtle ways, with more control.
If you have to do any processing in the main GUI thread, and you just want to update the interface, you may use the TWinControl.Repaint
method to redraw the GUI elements.
If you want to keep the app responsive to user input, you basically have to use backgroud/worker threads.
Notice: in Delphi, while doing any lenghty processing in the main thread, especcially if waiting is involved, you are supposed to call CheckSynchronize
periodically, to allow any other threads to synchonize with the main thread - they may (and probably will) hang otherwise.
VCL calls this only when the app goes idle and when it processes the WM_NULL
message (that is supossed to do nothing, which may cause some interesting side-effects too).
thank you all for your comments/suggestions.
here i made a test app that has a timer routine that takes longer than the interval it is set for. when i push button1, Application.ProcessMessages hangs. my solution for now is to disable the timer during the timer routine.
later we plan to put the "device communications" in a thread.
thank you! mp
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
Button1: TButton;
Memo1: TMemo;
procedure Timer1Timer(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
memo1.Lines.Add('text 1');
// this call blocks
Application.ProcessMessages;
memo1.Lines.Add('text 2');
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
iTime:cardinal;
begin
// fix by adding this: timer1.Enabled:=false;
iTime:=GetTickCount;
while GetTickCount-iTime<200 do
;
// fix by adding this: timer1.Enabled:=true;
end;
end.
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 286
ClientWidth = 426
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 8
Top = 56
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object Memo1: TMemo
Left = 112
Top = 8
Width = 297
Height = 233
Lines.Strings = (
'Memo1')
TabOrder = 1
end
object Timer1: TTimer
Interval = 100
OnTimer = Timer1Timer
Left = 200
Top = 144
end
Use madExcept and you will see where is the deadlock.
It seems terrible style to rely heavily on timer-based logic, and you understand that as you say you are planning to use threads in the future.
Here's the progression in your code:
- You have a timer window message. it does a small job.
- Six months later, your timer is running a lot of code.
- you think you could make things better by putting in Application.ProcessMessages.
Let's zoom in a little, on a timer with 100 msec interval, for a worst case:
09:00:00.000 - timer event fires
09:00:00.100 - we are half way through our Timer Event code. We hit an Application.ProcessMessages.
09:00:00.101 - timer event fires again, but the first time into it, has not yet completed.
09:00:00.200 - we are half way through our timer event code, the second time,
and hit APplication.ProcessMessages again.
Can you see the... Can you see the.... Can you see the..... problem here?
W
精彩评论