应用程序.ProcessMessages挂起了吗?

3

我的 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例程,这样做是没有意义的。

有什么一般性的建议吗?

我很惊讶在开发的这个阶段出现了这种问题!


6
为什么这是一个CW?对我来说,这似乎是一个相当具体和技术性的问题。 - Sertac Akyuz
@Sertac是正确的。除非A)您想邀请低声望用户编辑您的问题,或B)您正在询问高度主观的投票问题,否则您不必将问题标记为社区wiki。没有反对使您的问题成为CW的规定,但是您和回答它们的人都不会获得任何声望。 - Bill the Lizard
你为什么不考虑在你的应用程序中引入线程? - Lasse V. Karlsen
好的,了解社区维基很有用。我想说一些关于SO社区的事情。请把我视为一个偶尔使用者,对于如何分类问题(关闭问题、维基等)的“细节”基本上一无所知。SO对我来说是一个重要的工具,可以帮助我解决一些问题。从我的无知角度来看,“关闭”似乎是毫不留情地进行的。结果是,我发现自己花费时间思考问题可能会被“巡逻”的人们如何接受。我说这话可能不会让我受欢迎,但这是我的观点... - X-Ray
@Lasse V. Karlsen 是的 - 我计划稍后使用线程(几个月前我进行了概念验证)。由于我们已经晚了,我还没有将线程用于任何(重要的)事情,因此我采取行动较慢。 - X-Ray
4个回答

5

TApplication.ProcessMessages的建议是绝对不要使用它 - 没有一个好的点来放置它。

想象一下调用它会发生什么:您的应用程序运行消息循环 - 其中按顺序处理窗口消息(由操作系统,其他应用程序,您的应用程序等产生),然后在一个消息处理过程的中间,您重新运行整个消息循环,而没有控制哪些消息将被处理,其中有多少消息,如果任何消息将进入其自己的消息循环...以及它们是否存在任何重入问题。这就是我所说的招致麻烦

有时候有很好的理由处理一些窗口消息(特别是为了不挂起其他线程),或者处理所有发送到特定窗口的消息,但可以通过更微妙的方式,更有控制地实现这一点。

如果您必须在主GUI线程中进行任何处理,并且只想更新界面,则可以使用TWinControl.Repaint方法重新绘制GUI元素。

如果您想使应用程序对用户输入保持响应,则基本上必须使用背景/工作线程。

注意:在Delphi中,在主线程中进行任何漫长的处理,尤其是如果涉及等待,则应定期调用CheckSynchronize,以允许任何其他线程与主线程同步 - 否则它们可能会(并且可能会)挂起。
VCL仅在应用程序处于空闲状态且处理WM_NULL消息时调用此函数(其应该什么都不做,这也可能导致一些有趣的副作用)。


我同意Viktor Svuk所说的。如果你有一些需要进行长时间计算的代码部分,将它们放在主线程中并不是一个好主意。这是一种不良的编程实践。 - truthseeker

2

感谢大家的评论和建议。

我制作了一个测试应用程序,其中计时器例程所需时间超过了其设置的间隔。当我按下按钮1时,Application.ProcessMessages会挂起。目前我的解决方法是在计时器例程期间禁用计时器。

稍后我们计划将“设备通信”放入线程中。

谢谢! 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

1
使用madExcept,您将看到死锁发生的位置。

这是一个我之前没想到的想法,我原以为MadExcept无法捕获它,因为应用程序看起来并没有卡住,从很多方面来看,它似乎一切正常运行。 - X-Ray

0

在编程中,过度依赖基于计时器的逻辑似乎是可怕的风格,你明白这一点,因为你说你将来打算使用线程。

以下是你代码中的进展:

  1. 你有一个计时器窗口消息,它完成了一项小任务。
  2. 六个月后,你的计时器运行了很多代码。
  3. 你认为通过加入Application.ProcessMessages可以改善情况。

让我们放大一下,看看最坏情况下 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


是的,应用程序的早期版本使用了一个较慢(1000毫秒)的定时器。在这个开发周期的中途,他们告诉我了轮询速率。该项目的其他部分已经非常雄心勃勃,并且也已经拖延了……还没有时间去“进一步”并将其放入线程中——特别是因为我几乎没有线程方面的经验。谢谢你的评论! - X-Ray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接