我不明白 Delphi 中的 Application.ProcessMessages 在做什么 [英] I do not understand what Application.ProcessMessages in Delphi is doing

查看:18
本文介绍了我不明白 Delphi 中的 Application.ProcessMessages 在做什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Delphi 的菜鸟,很抱歉提出任何愚蠢的问题.

我的主管向我解释说 Application.ProcessMessages 可以防止应用程序冻结并分配一些额外的计算时间.但是在这个命令的文档中总是解释了一个被处理的队列系统?请有人解释一下上下文吗?

解决方案

正确回答这个问题没有捷径可走.

Windows 应用程序与操作系统交互的主要方式是通过消息传递系统.Windows 应用程序中发生的一切都是为了响应消息而发生的.

例如:

如果您点击屏幕,操作系统会决定点击了哪个应用程序并向该应用程序发送一条消息,表明它已收到点击(以及点击的位置).

如果移动窗口并显示其下方的应用程序的一部分,操作系统会发送一条消息,告诉您的应用程序重新绘制自身.

名单还在继续.发生的一切都是由消息驱动的.

现在,每个应用程序都有一个主要的用户界面线程(主"线程),这个线程有一个主要功能——它在无限循环中运行,检查来自操作系统的这些消息,然后执行必要的代码响应这些消息.

问题

你开始编写应用程序.你可能会写一些这样的代码:

procedure TForm1.Button1Click(Sender: TObject);var i : 整数;开始对于 i := 0 到 99999999999 开始解决世界的问题(i);计算PiToABillionPlaces;结尾;结尾;

在程序的较大结构中,在主线程中的执行如下所示:

  1. 检查消息
  2. 对于每条消息 - 执行相关的处理程序
  3. 返回检查消息(循环)

因此,当突然一个关联的处理程序(上面的Button1Click)开始花费很长时间才能完成时,这个循环很愉快.要理解的关键是,一个消息处理程序必须完成才能运行下一个.例如,如果您单击滚动条并拖动它,但您已将处理程序附加到 OnClick 需要 10 秒才能完成的滚动条,那么在单击处理程序完成之前,您的应用程序将看不到拖动操作.与此同时,消息队列正在填满,主线程没有做任何事情.

您肯定遇到过这种情况 - 突然您的应用程序对点击没有响应.你不能与它交互,你不能移动窗口,如果你在它上面拖动另一个窗口,应用程序甚至不会重新绘制自己 - 它只会填满你留在它上面的任何垃圾.

输入进程消息

不将长时间运行的代码放入线程的懒惰、糟糕的解决方案

当您调用 Application.ProcessMessages 时,您正在做的是,在这些处理程序之一的中间,指示主线程休息一下,返回检查消息队列并清空任何堆积如山的消息;处理任何点击、窗口移动、输入、击键、在需要时重新绘制自身等.

procedure TForm1.Button1Click(Sender: TObject);var i : 整数;开始对于 i := 0 到 99999999999 开始解决世界的问题(i);计算PiToABillionPlaces;Application.ProcessMessages;结尾;结尾;

从表面上看,这似乎是一件明智的事情,因为它允许您在长时间运行的循环中保持应用程序响应.然而,最终,这种编程风格被广泛认为是非常糟糕的实践,原因有很多.可以说,在任何您想使用 Application.ProcessMessages 的地方都是将工作转移到后台线程的可靠案例.

更多细节,让我们看看实际代码:

procedure TApplication.ProcessMessages;无功消息:TMsg;开始而 ProcessMessage(Msg) do {loop};结尾;

因此,当您调用 Application.ProcessMessages 时,您正在运行一个循环,即从消息队列中一个一个地清空消息(并执行附加到处理程序的所有代码)对这些消息做出反应)直到它为空.当它为空并且没有更多消息要处理时,控制权将返回到程序中的下一行.

需要注意的重要一点是,您在使用程序时体验到的流畅、流畅的交互完全是一种错觉,完全取决于该消息循环的处理速度.将消息发布到您的应用程序和处理消息之间的时间延迟越小,您的应用程序就越感觉它是活跃的和响应的.

正是因为这个原因,所有附加到用户界面处理程序的代码都应该快速运行.长时间运行的操作要么需要被中断,以便消息处理可以继续(即:Application.ProcessMessages),或者那些操作需要移动到一个单独的线程,在那里它们可以在不占用主线程的情况下执行和将其从其主要职责(即保持用户界面活跃)中移除.

<小时>

有关该主题的非常好的文章,请参阅:

A Key's Odyssey 作者 Peter Above

(互联网档案链接...万一上面死了)

摘要:本文介绍了击键消息通过 VCL 的路径.您将了解按键处理是如何实现的,OnKey 事件是如何工作的,以及在整个过程中可以找到程序员的哪些干预点.此外,还解释了消息处理等内容,您将了解如何在调试器中跟踪消息从消息循环到最终目的地.

I'm a noobie in Delphi, so sorry for any silly question.

My supervisor explained me that Application.ProcessMessages prevents freezing of the application and allocates some extra computing time. But in the docs of this command is always something explained about a queue system which is processed? Please can somebody explain me the context?

解决方案

There is no short way to answer this question properly.

The primary means by which Windows applications interact with the operating system is via a messaging system. Everything that happens in a windows application happens in response to a message.

For example :

If you click on the screen, the operating system decides which application got clicked and posts a message to that application indicating that it has received a click (along with the location of that click).

If a window is moved and reveals a part of your application beneath it, the operating system sends a message telling your application to repaint itself.

The list goes on. Everything that happens is driven by messages.

Now, each application has one primary user-interface thread (the "Main" thread) and this thread has one primary function - it runs in an infinite loop that checks for these messages from the operating system and then executes the necessary code in response to those messages.

The Problem

You come along and start writing an application. You might write some code like this :

procedure TForm1.Button1Click(Sender: TObject);
var i : Integer;
begin
  for i := 0 to 99999999999 do begin
    SolveTheProblemsOfTheWorld(i);
    CalculatePiToABillionPlaces;
  end;
end;

In the larger structure of the program, execution in the main thread looks like this:

  1. Check for messages
  2. For each message - execute the associated handlers
  3. Go back to Check for messages (loop)

So this loop is happily wizzing along when suddenly one of the associated handlers (Button1Click above) starts taking a very long time to complete. Key to understand is that one message handler must complete before the next one can run. If you click a scrollbar, for example, and drag it, but you've attached a handler to OnClick of the scrollbar that takes 10s to complete, then the drag operation will not be seen by your application until that click handler completes. In the meantime, the message queue is filling up and the main thread is not doing anything about it.

Surely you've experienced this - suddenly your application does not respond to clicks. You can't interact with it, you can't move the window, if you drag another window around overtop of it the application won't even repaint itself - it just fills up with whatever garbage you left on top of it.

Enter ProcessMessages

The lazy, terrible solution to not putting your long-running code into a thread

What you are doing when you are calling Application.ProcessMessages is, in the middle of one of these handlers, instructing the main thread to take a break to go back to check the message queue and empty any messages that have been piling up; to handle any clicks, window movements, inputs, keystrokes, to repaint itself if needed, etc.

procedure TForm1.Button1Click(Sender: TObject);
var i : Integer;
begin
  for i := 0 to 99999999999 do begin
    SolveTheProblemsOfTheWorld(i);
    CalculatePiToABillionPlaces;
    Application.ProcessMessages;
  end;
end;

This may seem superficially like a sensible thing to do since it allows you to keep the application responsive during a long-running loop. Ultimately, however, this style of programming is widely considered to be extremely poor practice for a large number of very good reasons. Suffice it to say that anywhere you are tempted to use Application.ProcessMessages is a solid case for moving that work to a background thread instead.

For more detail, let's have a look at the actual code :

procedure TApplication.ProcessMessages;
var
  Msg: TMsg;
begin
  while ProcessMessage(Msg) do {loop};
end;

So when you make this call to Application.ProcessMessages you are running a loop that is, one by one, emptying messages from the message queue (and executing all of the code that is attached to handlers that are reacting to those messages) until it is empty. When it is empty and there are no more messages to process, control will return to the next line in your program.

The important point to take away is that the fluid, smooth interaction you experience when using a program is a complete illusion that depends exactly on this message loop being processed as quickly as possible. The smaller the time delay between a message being posted to your application and the message being handled, the more your application will feel like it is alive and responsive.

It is for this reason that all code that is attached to user-interface handlers should be fast running. Long running operations either need to be interrupted so that message handling can continue (ie: Application.ProcessMessages) or those operations need to be moved to a separate thread where they can execute without tying up the main thread and taking it away from its primary responsibility (which is to keep the user-interface alive).


For a really good article on the topic, see :

A Key's Odyssey by Peter Below

(Internet Archive link... in case the above dies)

Abstract: This article follows the path of a keystroke message through the VCL. You will learn how the key processing is implemented, how the OnKey events work and what intervention points for the programmer can be found in the whole process. In addition, things like message processing are explained, and you will learn how to trace messages in the debugger from the message loop to their eventual destination.

这篇关于我不明白 Delphi 中的 Application.ProcessMessages 在做什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆