我有一个程序,它使用几个线程来执行某些任务.每个线程都有一堆任务要执行.在执行其中一个之后,每个thred将向主屏幕调用一条帖子消息来更新日志.
现在我有六万个任务,每个线程一万个 - 六个线程 - 在执行每个任务线程后调用post消息.但由于这些帖子消息,我的应用程序变得非常繁忙,看起来像是被绞死了.
如果我删除帖子消息......每件事情都可以.但是我无法直接调用该过程,因为它使用ui控件并且ui控件不是线程安全的,并且直接从线程调用过程将导致其他错误.
那么有什么替代品可用于postmessage和发送消息.
谢谢,bASIL
Ian*_*oyd 23
问题是发布消息有两个消息队列.这样做的结果是,你发布的消息之前的任何总是被处理Paint,Input或Timer消息.
这意味着您将使用几十条消息充斥消息队列.这些消息将始终在绘制和用户消息之前得到处理 - 使您的应用程序看起来挂起.
解决这个问题的常用方法是使用计时器; 让你的代码启动一个非常短的持续时间(例如0毫秒)计时器.
澄清
定时器消息(
WM_TIMER),如Paint消息(WM_PAINT)和输入消息(例如WM_MOUSEMOVE,WM_KEYDOWN)在发布消息后处理.具体的定时器消息在输入和绘制消息后处理.这意味着您的应用程序将在处理下
WM_TIMER一条消息之前响应用户事件和绘制请求.您可以通过使用Timer(及其WM_TIMER消息)来利用此行为,而不是自己发布消息(这将始终优先于绘制和输入消息).
当计时器开火时,他们会发送WM_TIMER信息.任何输入和绘制消息后,将始终处理此消息; 使您的应用程序显示响应.
设置计时器的另一个好处是它会强制您"排队"在(线程安全)列表中处理的所有项目.您将几千个工作项组合成一个"计时器",从而使系统不必生成和处理数千个不必要的消息.
...消息按以下顺序处理:
更新:你应该不会有一个计时器投票,那只是浪费和错误的.你的线程应该" 设置一个标志 "(即计时器).这允许你的主线程实际上空闲,只有在有事情要做时才会醒来.
更新二:
表示不保留消息生成顺序的伪代码:
//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, 0);
end;
else
DefWindowProc(g_dc, Message.Msg, Message.wParam, Message.lParam);
end;
end;
Run Code Online (Sandbox Code Playgroud)
即使我生成WM_ProcessNextItem 后,我产生一个WM_PAINT消息,(即Invalidate)时,WM_PAINT将永远不会得到处理,因为总是收到其它投送消息.而作为MSDN说,绘制消息只会如果没有其他发布的消息出现.
更新三:是的,只有消息队列,但这就是我们不关心的原因:
发送和发布的消息
我将在这里使用的术语是非标准的,但我正在使用它,因为我认为它比标准术语更清晰.出于本讨论的目的,我将说明与线程相关的消息分为三个桶而不是更标准的两个桶:
Run Code Online (Sandbox Code Playgroud)What I'll call them Standard terminology =========================== ============================= Incoming sent messages Non-queued messages Posted messages \_ Input messages / Queued messages实际上,消息细分比这更复杂,但我们暂时坚持使用上述模型,因为它"足够真实".
旧的新事物,整个Windows演变的实践发展作者:
Raymond Chen
ISBN 0-321-44030-7
版权所有©2007 Pearson Education,Inc.
第15章 - 如何传递和检索窗口消息,第358页
更容易想象有两个消息队列.在"第一个"队列为空之前,不会读取"第二个"中的消息; OP永远不会让第一个队列流失.因此,第二个队列中的"绘制"和"输入"消息都没有被处理,使应用程序看起来挂起.
这是对实际情况的简化,但它足够接近本讨论的目的.
更新四
问题不一定是你用你的消息"淹没"输入队列.只有一条消息,您的应用程序可能无响应.只要队列中有一条已发布的消息,就会在任何其他消息之前处理它.
想象一下发生了一系列事件:
WM_MOUSEMOVE)WM_LBUTTONDOWN)WM_LBUTTONUP)WM_PAINT)WM_ProcessNextItem)您的应用程序的主消息循环(调用GetMessage)将不会按照它们发生的顺序接收消息.它将检索WM_ProcessNextItem消息.这将从队列中删除消息,留下:
WM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_PAINT当您处理项目时,用户会再移动鼠标,并随机点击:
WM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_PAINTWM_MOUSEMOVEWM_MOUSEMOVEWM_MOUSEMOVEWM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_MOUSEMOVEWM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_MOUSEMOVEWM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUP为了回应你,WM_ProcessNextItem你发回另一条消息给自己.这样做是因为您希望在继续处理更多项目之前先处理未完成的消息.这将向队列添加另一条发布的消息:
WM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_PAINTWM_MOUSEMOVEWM_MOUSEMOVEWM_MOUSEMOVEWM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_MOUSEMOVEWM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_MOUSEMOVEWM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_ProcessNextItem问题开始变得明显.下一次调用GetMessage将检索WM_ProcessNextItem,为应用程序留下积压的绘制和输入消息:
WM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_PAINTWM_MOUSEMOVEWM_MOUSEMOVEWM_MOUSEMOVEWM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_MOUSEMOVEWM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUPWM_MOUSEMOVEWM_MOUSEMOVEWM_LBUTTONDOWNWM_LBUTTONUP解决方案是利用消息的无序处理.始终在Paint/Input/Timer消息之前处理已发布消息:不使用已发布消息.你可以想到的消息队列为被分为两组:发布消息和输入消息.而不是导致永远不允许"已发布"消息队列被清空的情况:
Posted messages Input messages
================== =====================
WM_ProcessNextItem WM_MOUSEMOVE
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_PAINT
Run Code Online (Sandbox Code Playgroud)
你使用一条WM_TIMER消息:
Posted messages Input messages
================== =====================
WM_MOUSEMOVE
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_PAINT
WM_TIMER
Run Code Online (Sandbox Code Playgroud)
Nitpickers Corner:这两个队列的描述并不严格,但这是真的.Windows如何以记录的顺序传递消息是内部实现细节,可随时更改.
重要的是要注意你没有用计时器进行轮询,这将是不好的.相反,你正在发射一次性定时器作为生成WM_TIMER消息的机制.你这样做是因为你知道定时器消息不会优先于绘制或输入消息.
使用计时器具有另一个可用性优势.在WM_PAINT,WM_TIMER和输入消息之间,还有对它们的无序处理:
WM_PAINT, 然后WM_TIMER如果您使用计时器通知主线程,您还可以保证您将更快地处理绘画和用户输入.这可确保您的应用程序保持响应.这是一个可用性增强,你可以免费获得它.
您将很难找到比 PostMessage 更好的东西。我的猜测是您的问题是您更新 UI 过于频繁,并且您的队列变得饱和,因为您无法足够快地为其提供服务。如果你更新不到一秒钟,跳过更新怎么样?如果这恢复了响应能力,那么您可以考虑更强大的解决方案。