Application.ProcessMessages挂起?

时间:2010-06-11 23:35:04

标签: delphi delphi-2009 vcl

我的单线程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例程让人联想到一些无用的东西。

任何一般性建议?

我很惊讶地看到在开发的这一点出现了这种问题!

5 个答案:

答案 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)

严重依赖基于计时器的逻辑似乎很糟糕,并且您理解正如您所说,您计划在未来使用线程。

以下是代码中的进展:

  1. 您有一个计时器窗口消息。它做得很少。
  2. 六个月后,您的计时器正在运行大量代码。
  3. 你认为通过加入Application.ProcessMessages可以让事情变得更好。
  4. 在最坏的情况下,让我们在一个间隔为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 ^