delphi – Postmessage和sendmessage的替代方案

前端之家收集整理的这篇文章主要介绍了delphi – Postmessage和sendmessage的替代方案前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个程序正在使用几个线程执行一些任务.每个线程都有一堆要执行的任务.在执行其中的一个后,每个thred将调用一条发送消息到主屏幕来更新日志.

现在我有六万个任务,每个线程一万个 – 六个线程 – 执行每个任务线程后,调用帖子消息.但是由于这些帖子消息,我的应用程序变得非常繁忙,看起来像挂起.

如果我删除帖子消息…每件事情都可以正常工作.但我不能直接调用程序,因为它使用ui控件和ui控件不是线程安全的,直接从线程调用过程将导致其他错误.

那么还有什么替代方案可以用于postmessage和发送消息.

谢谢,
罗勒

解决方法

问题是发布邮件有两个消息队列.其结果是您发布的消息是 always processed before any Paint,Input,or Timer messages.

这意味着您正在使用几十万条消息淹没消息队列.这些消息将始终在绘制和用户消息之前被处理 – 使您的应用程序看起来挂起.

解决这个问题的常用方法是使用定时器;您的代码开始时间非常短(例如0 ms)定时器.

Clarification

Timer messages (WM_TIMER),like Paint messages (WM_PAINT) and Input messages (e.g. WM_MOUSEMOVE,WM_KEYDOWN) are processed after posted messages. Timer messages specifically are processed after input and paint messages.

This means that your application will respond to user events and paint requests before it handles the next WM_TIMER message. You can harness this behavior by using a Timer (and it’s WM_TIMER message),rather than posting a message yourself (which would always take priority over paint and input messages).

定时器触发时,发送WM_TIMER消息.任何输入和绘制消息后,将始终处理此消息;使您的应用程序显示响应.

设置计时器的另一个优点是它强制您“排队”所有在(线程安全)列表中处理的项目.您将几千个工作项目合并成一个“定时器”,从而节省系统不必生成和处理数千个不必要的消息.

奖金喋喋不休

…messages are processed in the following order:

  • Sent messages
  • Posted messages
  • Input (hardware) messages and system internal events
  • Sent messages (again)
  • 07001 messages
  • 07002 messages

更新:你不应该有一个定时器轮询,这只是浪费和错误.你的线程应该“设置一个标志”(即定时器).这允许您的主线程实际上空闲,只有当有事要做的时候醒来.

更新二:

证明消息生成顺序不被保留的伪代码

//Code is public domain. No attribution required.
const
  WM_ProcessNextItem = WM_APP+3;

procedure WindowProc(var Message: TMessage)
begin
   case Message.Msg of
   WM_Paint: PaintControl(g_dc);
   WM_ProcessNextItem:
      begin
          ProcessNextItem();
          Self.Invalidate; //Invalidate ourselves to trigger a wm_paint

          //Post a message to ourselves so that we process the next 
          //item after any paints and mouse/keyboard/close/quit messages
          //have been handled
          PostMessage(g_dc,WM_ProcessNextItem,0); 
      end;
   else
      DefWindowProc(g_dc,Message.Msg,Message.wParam,Message.lParam);
   end;
end;

即使在生成WM_PAINT消息(即无效)后生成WM_ProcessNextItem,WM_PAINT也不会被处理,因为在它之前总是有一个发送的消息.而as MSDN says,只有没有其他帖子的消息才会出现.

更新三:是的,只有消息队列,但这就是为什么我们不在乎:

Sent and posted messages

The terminology I will use here is nonstandard,but I’m using it because I think it’s a little clearer than the standard terminology. For the purpose of this discussion,I’m going to say that the messages associated with a thread fall into three buckets rather than the more standard two:

06001

In reality,the message breakdown is more complicated than this,but we’ll stick to the above model for now,because it’s “true enough.”

旧的新事物,Windows的进化中的实践发展
陈冯富珍
ISBN 0-321-44030-7
版权所有©2007 Pearson Education,Inc.
第15章 – 如何发送和检索窗口消息,第358页

更容易想象有两个消息队列.在“第一”队列为空之前,将不会读取“第二”中的消息;而OP绝对不会让第一个队列流失.因此,第二个队列中的“paint”和“input”消息都不会被处理,从而使应用程序看起来挂起.

This is a simplification of what actually goes on,but it’s close enough for the purposes of this discussion.

更新四

问题不一定是您的邮件“淹没”输入队列.您的应用程序可能无法响应,只有一条消息.只要你有一个帖子在队列中,它将在任何其他消息之前被处理.

想象一下,一系列事件发生:

鼠标移动(WM_MOUSEMOVE)
>鼠标左键被按下(WM_LBUTTONDOWN)
>鼠标左键已释放(WM_LBUTTONUP)
>用户移动另一个窗口,导致您的应用程序需要重新绘制(WM_PAINT)
>你的线程有一个项目准备好,并发布通知(WM_ProcessNextItem)

您的应用程序的main message loop(调用GetMessage)将不会按照发生的顺序接收消息.它将检索WM_ProcessNextItem消息.这将从消息队列中移除消息,留下:

> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_PAINT

处理您的项目时,用户可以更多地移动鼠标,并随机点击:

> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_PAINT
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP

响应您的WM_ProcessNextItem,您再发送一个消息给自己.您这样做是因为您希望先处理未完成的邮件,然后再继续处理更多的项目.这将添加另一个发送消息到队列中:

> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_PAINT
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_ProcessNextItem

问题开始变得明显.下一次调用GetMessage将检索WM_ProcessNextItem,使应用程序有一个积压的油漆和输入消息:

> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_PAINT
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP
> WM_MOUSEMOVE
> WM_MOUSEMOVE
> WM_LBUTTONDOWN
> WM_LBUTTONUP

解决方案是利用消息的乱序处理.发布的消息始终在绘制/输入/定时器消息之前处理:不要使用已发布的消息.您可以将消息队列分为两组:发布消息和输入消息.而不是导致“发布”消息队列从不允许为空的情况:

Posted messages      Input messages
==================   =====================
WM_ProcessNextItem   WM_MOUSEMOVE
                     WM_LBUTTONDOWN
                     WM_LBUTTONUP
                     WM_PAINT

你使用WM_TIMER消息:

Posted messages      Input messages
==================   =====================
                     WM_MOUSEMOVE
                     WM_LBUTTONDOWN
                     WM_LBUTTONUP
                     WM_PAINT
                     WM_TIMER

Nitpickers Corner: This description of two queues isn’t strictly true,but it’s true enough. How Windows delivers messages in the documented order is an internal implementation detail,subject to change at any time.

重要的是要注意,你不是用计时器进行轮询,这将是坏的.相反,您正在触发一次性定时器作为生成WM_TIMER消息的机制.您这样做是因为您知道定时器消息不会优先于画面或输入消息.

使用定时器具有另一个可用性优势.在WM_PAINT,WM_TIMER和输入消息之间,它们也有无序处理:

>输入消息,然后
> WM_PAINT,然后
> WM_TIMER

如果您使用计时器通知您的主线程,还可以保证您更快地处理油漆和用户输入.这样可以确保您的应用程序保持响应.这是一个可用性增强,你可以免费获得它.

猜你在找的Delphi相关文章