Application.ProcessMessages 挂起?

发布于 2024-09-05 12:53:03 字数 770 浏览 2 评论 0原文

我的单线程 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 例程在我的脑海中浮现出一些没有意义的东西。

有什么一般性建议吗?

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

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.ProcessMessages 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

热血少△年 2024-09-12 12:53:03

我对 TApplication.ProcessMessages 的建议是永远不要使用它 - 根本没有放置它的好点。

想象一下调用它会做什么:您的应用程序运行一个消息循环 - 其中 Windows 消息(由操作系统、其他应用程序、您的应用程序等生成)被顺序处理 - 并且在其中一个消息处理的中间,您只需再次重新运行整个消息循环,无法控制将处理哪些消息、将有多少消息、是否有任何消息将进入其自己的消息循环......以及它们是否有任何重入问题。这就是我所说的自招麻烦

有时有很好的理由来处理一些窗口消息(特别是不挂起其他线程),或者处理所有定向到的消息一个特定的窗口,但这可以通过更微妙的方式和更多的控制来完成。

如果您必须在主 GUI 线程中进行任何处理,并且您只想更新界面,您可以使用 TWinControl.Repaint 方法重绘 GUI 元素。
如果您想让应用程序响应用户输入,您基本上必须使用后台/工作线程。

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

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).

尴尬癌患者 2024-09-12 12:53:03

谢谢大家的意见/建议。

在这里,我制作了一个测试应用程序,它有一个计时器例程,该例程花费的时间比设置的时间间隔更长。当我按下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

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
能否归途做我良人 2024-09-12 12:53:03

使用 madExcept 你就会看到死锁在哪里。

Use madExcept and you will see where is the deadlock.

ˉ厌 2024-09-12 12:53:03

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

以下是代码的进展:

  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.

你能看到... 你能看到... 你能看到... 这里的问题吗?

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:

  1. You have a timer window message. it does a small job.
  2. Six months later, your timer is running a lot of code.
  3. 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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文