我的单线程delphi 2009应用程序(尚未完成)已经开始出现Application.ProcessMessages挂起的问题。我的应用程序有一个TTimer对象,每100毫秒触发一次以轮询外部设备。我使用Application.ProcessMessages在更改内容时更新屏幕,以便应用程序仍然响应。
其中一个是在网格OnMouseDown事件中。在那里,它有一个基本上挂起的Application.ProcessMessages。除了我很快发现另一个同样阻塞的Application.ProcessMessages之外,删除它没有问题。
我认为可能发生的事情是,TTimer是 - 在应用程序模式下我正在调试 - 可能需要很长时间才能完成。我已经阻止TTimer.OnTimer事件处理者重新输入相同的代码(见下文):
procedure TfrmMeas.tmrCheckTimer(Sender: TObject);
begin
if m_CheckTimerBusy then
exit;
m_CheckTimerBusy:=true;
try
PollForAndShowMeasurements;
finally
m_CheckTimerBusy:=false;
end;
end;
调用Application.ProcessMessages会有什么不好的做法? OnPaint例程让人联想到一些无用的东西。
任何一般性建议?
我很惊讶地看到在开发的这一点出现了这种问题!
答案 0 :(得分:4)
我对TApplication.ProcessMessages
的建议是永远不会使用它 - 只是放置它不是一个好点。
想象一下调用它的作用:你的应用程序运行一个消息循环 - 其中windows消息(由操作系统,其他应用程序,你的应用程序等产生)被顺序处理 - 并且在其中一个消息处理过程中,你只需要重新运行整个消息循环,而无需控制将处理哪些消息,将有多少消息,如果有任何消息将进入其自己的消息循环......以及它们是否有任何重入问题与否。这就是我所说的邀请麻烦。
有时候有充分的理由处理某些 Windows消息(特别是不挂起其他线程),或处理指向特定窗口的所有消息,但这可能会以更微妙的方式完成,更多的控制。
如果必须在主GUI线程中执行任何处理,并且您只想更新界面,则可以使用TWinControl.Repaint
方法重新绘制GUI元素。 />
如果你想让应用程序响应用户输入,你基本上必须使用backgroud / worker threads。
注意:在Delphi中,在主线程中执行任何长度处理时,特别是如果涉及等待,则应该定期调用CheckSynchronize
,以允许任何其他线程与主线程同步 - 它们可能(并且可能会)否则
只有当应用程序进入空闲状态并处理WM_NULL
消息时(通常不会做任何事情,这可能会导致一些有趣的副作用),VCL会调用它。
答案 1 :(得分:2)
谢谢大家的意见/建议。
这里我做了一个测试应用程序,它有一个计时器例程,花费的时间比设置的时间长。当我按下button1时,Application.ProcessMessages挂起。我现在的解决方案是在计时器例程中禁用计时器。
稍后我们计划将“设备通信”放在一个帖子中。
谢谢你! 熔点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
答案 2 :(得分:1)
我推荐这篇文章:“How to debug application’s hang?”;)
答案 3 :(得分:1)
使用madExcept,你会看到死锁的位置。
答案 4 :(得分:0)
严重依赖基于计时器的逻辑似乎很糟糕,并且您理解正如您所说,您计划在未来使用线程。
以下是代码中的进展:
在最坏的情况下,让我们在一个间隔为100毫秒的定时器上放大一点:
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.
你能看到......你能看到....你能看到.....问题吗?
w ^