Delphi应用程序中Application.ProcessMessages的阴暗面

Article submitted by Marcus Junglas

Marcus Junglas提交的文章

When programming an event handler in Delphi (like the OnClick event of a TButton), there comes the time when your application needs to be busy for a while, e.g. the code needs to write a big file or compress some data.

在Delphi中对事件处理程序进行编程时(例如TButton的OnClick事件),有时会出现应用程序需要忙一段时间的情况,例如代码需要编写一个大文件或压缩一些数据。

If you do that you'll notice that your application seems to be locked. Your form cannot be moved anymore and the buttons are showing no sign of life. It seems to be crashed.

如果这样做,您会发现您的应用程序似乎已被锁定 。 您的表格无法再移动了,按钮也没有显示任何生命迹象。 它似乎已崩溃。

The reason is that a Delpi application is single threaded. The code you are writing represents just a bunch of procedures which are called by Delphi's main thread whenever an event occured. The rest of the time the main thread is handling system messages and other things like form and component handling functions.

原因是Delpi应用程序是单线程的。 您正在编写的代码仅代表一堆过程,只要事件发生,Delphi的主线程就会调用这些过程。 其余时间,主线程正在处理系统消息以及其他诸如表单和组件处理功能之类的东西。

So, if you don't finish your event handling by doing some lengthy work, you will prevent the application to handle those messages.

因此,如果您不通过做一些冗长的工作来完成事件处理,则将阻止应用程序处理这些消息。

A common solution for such type of problems is to call "Application.ProcessMessages". "Application" is a global object of the TApplication class.

此类问题的常见解决方案是调用“ Application.ProcessMessages”。 “应用程序”是TApplication类的全局对象。

The Application.Processmessages handles all waiting messages like window movements, button clicks and so on. It is commonly used as a simple solution to keep your application "working".

Application.Processmessages处理所有等待的消息,例如窗口移动,按钮单击等。 它通常用作保持应用程序“运行”的简单解决方案。

Unfortunately the mechanism behind "ProcessMessages" has its own characteristics, which might cause big confusion!

不幸的是,“ ProcessMessages”背后的机制有其自身的特征,这可能会引起很大的混乱!

什么是ProcessMessages? ( What does ProcessMessages? )

PprocessMessages handles all waiting system messages in the applications message queue. Windows uses messages to "talk" to all running applications. User interaction is brought to the form via messages and "ProcessMessages" handles them.

PprocessMessages处理应用程序消息队列中所有正在等待的系统消息。 Windows使用消息与所有正在运行的应用程序“对话”。 用户交互通过消息传递到表单,“ ProcessMessages”处理它们。

If the mouse is going down on a TButton, for example, ProgressMessages does all what should happen on this event like the repaint of the button to a "pressed" state and, of course, a call to the OnClick() handling procedure if you assigned one.

例如,如果鼠标移至TButton上,ProgressMessages会执行此事件上应发生的所有操作,例如将按钮重新绘制为“按下”状态,当然,如果需要,还可以调用OnClick()处理过程分配一个。

That's the problem: any call to ProcessMessages might contain a recursive call to any event handler again. Here's an example:

这就是问题所在:对ProcessMessages的任何调用都可能再次包含对任何事件处理程序的递归调用。 这是一个例子:

Use the following code for a button's OnClick even handler ("work"). The for-statement simulates a long processing job with some calls to ProcessMessages every now and then.

将以下代码用于按钮的OnClick偶数处理程序(“工作”)。 for语句不时地通过对ProcessMessages的一些调用来模拟长时间的处理工作。

This is simplified for better readability:

对此进行了简化以提高可读性:

 {in MyForm:}
  WorkLevel : integer;{OnCreate:}
  WorkLevel := 0;procedure TForm1.WorkBtnClick(Sender: TObject) ;var
  cycle : integer;begin
  inc(WorkLevel) ;for cycle := 1 to 5 dobegin
    Memo1.Lines.Add('- Work ' + IntToStr(WorkLevel) + ', Cycle ' + IntToStr(cycle) ;Application.ProcessMessages;
    sleep(1000) ; // or some other workend;
  Memo1.Lines.Add('Work ' + IntToStr(WorkLevel) + ' ended.') ;
  dec(WorkLevel) ;end;

WITHOUT "ProcessMessages" the following lines are written to the memo, if the Button was pressed TWICE in a short time:

如果没有在短时间内两次按下按钮,则没有“ ProcessMessages”的情况下,以下行将写入备忘录:

 - Work 1, Cycle 1
- Work 1, Cycle 2
- Work 1, Cycle 3
- Work 1, Cycle 4
- Work 1, Cycle 5
Work 1 ended.
- Work 1, Cycle 1
- Work 1, Cycle 2
- Work 1, Cycle 3
- Work 1, Cycle 4
- Work 1, Cycle 5
Work 1 ended.

While the procedure is busy, the the form does not show any reaction, but the second click was put into the message queue by Windows. Right after the "OnClick" has finished it will be called again.

在此过程繁忙时,该表单不会显示任何React,但是Windows会将第二次单击放入了消息队列。 “ OnClick”完成后,将立即再次调用它。

INCLUDING "ProcessMessages", the output might be very different:

包括“ ProcessMessages”,输出可能会非常不同:

 - Work 1, Cycle 1
- Work 1, Cycle 2
- Work 1, Cycle 3
- Work 2, Cycle 1
- Work 2, Cycle 2
- Work 2, Cycle 3
- Work 2, Cycle 4
- Work 2, Cycle 5
Work 2 ended.
- Work 1, Cycle 4
- Work 1, Cycle 5
Work 1 ended.

This time the form seems to be working again and accepts any user interaction. So the button is pressed half way during your first "worker" function AGAIN, which will be handled instantly. All incoming events are handled like any other function call.

这次,表单似乎可以再次使用并接受任何用户交互。 因此,在您的第一个“工人”功能再次使用时,将按钮按下一半,该功能将立即处理。 与所有其他函数调用一样,处理所有传入事件。

In theory, during every call to "ProgressMessages" ANY amount of clicks and user messages might happen "in place".

理论上,在每次“ ProgressMessages”调用期间,任何数量的点击和用户消息都可能“就地”发生。

So be careful with your code!

因此,请注意您的代码!

Different example (in simple pseudo-code!):

不同的示例(使用简单的伪代码!):

 procedure OnClickFileWrite() ;var myfile := TFileStream;begin
  myfile := TFileStream.create('myOutput.txt') ;trywhile BytesReady > 0 dobegin
      myfile.Write(DataBlock) ;
      dec(BytesReady,sizeof(DataBlock)) ;
      DataBlock[2] := #13; {test line 1}Application.ProcessMessages;
      DataBlock[2] := #13; {test line 2}end;finally
    myfile.free;end;end;

This function writes a large amount of data and tries to "unlock" the application by using "ProcessMessages" each time a block of data is written.

该函数将写入大量数据,并在每次写入数据块时尝试使用“ ProcessMessages”来“解锁”应用程序。

If the user clicks on the button again, the same code will be executed while the file is still being written to. So the file cannot be opened a 2nd time and the procedure fails.

如果用户再次单击该按钮,则仍在写入文件时将执行相同的代码。 因此,该文件无法第二次打开,并且该过程失败。

Maybe your application will do some error recovery like freeing the buffers.

也许您的应用程序将进行一些错误恢复,例如释放缓冲区。

As a possible result "Datablock" will be freed and the first code will "suddenly" raise an "Access Violation" when it accesses it. In this case: test line 1 will work, test line 2 will crash.

作为可能的结果,“数据块”将被释放,并且第一个代码在访问它时将“突然”引发“访问冲突”。 在这种情况下:测试线1将起作用,测试线2将崩溃。

The better way:

更好的方法:

To make it easy you could set the whole Form "enabled := false", which blocks all user input, but does NOT show this to the user (all Buttons are not grayed).

为了简单起见,您可以将整个表单设置为“ enabled:= false”,该表单将阻止所有用户输入,但不会向用户显示此信息(所有按钮均不显示为灰色)。

A better way would be to set all buttons to "disabled", but this might be complex if you want to keep one "Cancel" button for example. Also you need to go through all the components to disable them and when they are enabled again, you need to check if there should be some remaining in the disabled state.

更好的方法是将所有按钮都设置为“禁用”,但是如果要保留一个“取消”按钮,则此操作可能很复杂。 另外,您需要检查所有组件以禁用它们,并且在再次启用它们时,需要检查是否还有一些剩余的处于禁用状态。

You could disable a container child controls when the Enabled property changes.

您可以在Enabled属性更改时禁用容器子控件

As the class name "TNotifyEvent" suggests, it should only be used for short term reactions to the event. For time consuming code the best way is IMHO to put all the "slow" code into an own Thread.

正如类名“ TNotifyEvent”所暗示的那样,它仅应用于对该事件的短期React。 对于耗时的代码,最好的方法是恕我直言,将所有“慢速”代码放入自己的线程中。

Regarding the problems with "PrecessMessages" and/or the enabling and disabling of components, the usage of a second thread seems to be not too complicated at all.

关于“ PrecessMessages”和/或启用和禁用组件的问题, 第二个线程的使用似乎一点也不复杂。

Remember that even simple and fast lines of code might hang for seconds, e.g. opening a file on a disc drive might have to wait until the drive spin up has finished. It doesn't look very good if your application seem to crash because the drive is too slow.

请记住,即使是简单快速的代码行也可能会挂起几秒钟,例如,在磁盘驱动器上打开文件可能必须等到驱动器旋转完成后才能进行。 如果您的应用程序似乎由于驱动器速度太慢而崩溃,那看起来就不太好。

That's it. The next time you add "Application.ProcessMessages", think twice ;)

而已。 下次添加“ Application.ProcessMessages”时,请三思;)

翻译自: https://www.thoughtco.com/dark-side-of-application-processmessages-1058203

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值