现在我有六万个任务,每个线程一万个 – 六个线程 – 执行每个任务线程后,调用帖子消息.但是由于这些帖子消息,我的应用程序变得非常繁忙,看起来像挂起.
如果我删除帖子消息…每件事情都可以正常工作.但我不能直接调用程序,因为它使用ui控件和ui控件不是线程安全的,直接从线程调用过程将导致其他错误.
那么还有什么替代方案可以用于postmessage和发送消息.
谢谢,
罗勒
解决方法
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’sWM_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
如果您使用计时器通知您的主线程,还可以保证您更快地处理油漆和用户输入.这样可以确保您的应用程序保持响应.这是一个可用性增强,你可以免费获得它.