替代邮箱和邮寄 [英] Alternative for Postmessage and sendmessage

查看:123
本文介绍了替代邮箱和邮寄的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个程序正在使用几个线程执行一些任务。每个线程都有一堆要执行的任务。在执行其中一个后,每个thred将会在主屏幕上发送一条消息到更新日志。



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



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





谢谢,
bASIL

解决方案

div>

问题是发送消息有两个消息队列。这样做的结果是,您的发布的消息是 Paint ,输入定时器消息。



这意味着您正在使用数十万条消息溢出消息队列。这些消息将始终在绘制和用户消息之前被处理 - 使您的应用程序看起来挂起。



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


澄清



定时器消息( WM_TIMER ),例如Paint消息( WM_PAINT )和输入消息(例如WM_MOUSEMOVE,WM_KEYDOWN) em>发布消息。定时器消息具体在输入和绘制消息之后被处理。



这意味着您的应用程序将在处理下一个 WM_TIMER c $ c>消息。您可以通过使用计时器(它是 WM_TIMER 消息)来利用此行为,而不是自己发布消息(这将始终优先于画面和输入消息)。 p>

当定时器触发时,他们发送一个 WM_TIMER 消息。在任何输入和绘画消息之后,这个消息总是被处理使您的应用程序显示响应。



设置定时器的另一个优势在于它强制您排队所有已处理的项目(线程安全)列表。您将几千个工作项目合并成一个计时器,从而无需生成和处理数千个不必要的邮件,从而节省系统费用。



Bonus Chatter




...邮件按以下顺序处理:




  • 已发送消息

  • 发布消息

  • 输入(硬件)消息和系统内部事件

  • 发送消息(再次)

  • WM_PAINT 信息

  • WM_TIMER 消息


更新:您应该有一个定时器轮询,这只是浪费和错误。你的线程应该设置一个标志(即定时器)。这可以让你的线程实际上闲置,只有当有事要做时才醒来。



更新两个



表示消息生成顺序不被保留的伪代码:

  //代码是公有领域无需归因。 
const
WM_ProcessNextItem = WM_APP + 3;

程序WindowProc(var Message:TMessage)
begin
case Message.Msg
WM_Paint:PaintControl(g_dc);
WM_ProcessNextItem:
begin
ProcessNextItem();
Self.Invalidate; //无效我们自己触发一个wm_paint

//向我们自己发送一个消息,以便我们在任何绘画和鼠标/键盘/关闭/退出消息之后处理下一个
//项目
//已处理
PostMessage(g_dc,WM_ProcessNextItem,0,0);
结束
else
DefWindowProc(g_dc,Message.Msg,Message.wParam,Message.lParam);
结束
结束

即使我生成了一个 WM_ProcessNextItem 后,我生成一个 WM_PAINT 消息(即无效), WM_PAINT 永远不会被处理,因为在它之前有总是另一个发布的消息。而作为MSDN说,只有没有其他发布的消息才会出现绘画消息。



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


发送和发布的消息



术语I这里使用的是非标准的,但是我使用它是因为我认为它比标准术语更清晰一点。为了讨论的目的,我要说的是,与线程相关联的消息分为三个桶,而不是更标准的两个:

 code>我称之为标准术语
========================= ==== =====================
传入的邮件非排队消息
发布消息\_
输入消息/排队的消息

在现实中,消息分解比这更复杂,但我们会坚持以前的模型,因为它是真实的。


旧的新事物,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_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



解决方案是利用消息的乱序处理s。发布的消息始终在绘制/输入/定时器消息之前处理:不要使用已发布的消息。您可以将消息队列的 分为两组:发布消息和输入消息。而不是导致发布消息队列永远不会被清空的情况:

 发布消息输入消息
================== =====================
WM_ProcessNextItem WM_MOUSEMOVE
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_PAINT

您使用 WM_TIMER 消息:

 发布消息输入消息
========= ========= ===================
WM_MOUSEMOVE
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_PAINT
WM_TIMER




Nitpickers Corner :这两个队列的描述不是严格的,但它是真的。如何Windows以文档化的顺序发送消息是一个内部实现细节,可随时更改。


重要的是要注意你用定时器'不是轮询,这将是坏的。相反,您将触发一次性定时器作为生成 WM_TIMER 消息的机制。您这样做是因为您知道定时器消息不会优先于油漆输入消息。



使用定时器具有另一个可用性优势。在 WM_PAINT WM_TIMER 输入消息之间,有无序处理对他们也是如此:




  • 输入消息,然后

  • code> WM_PAINT ,然后

  • WM_TIMER



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


      i have a program which is using several threads to execute some task. Each thread is having a bunch of task to execute. after executing one of them, each thred will call a post message to main screen to update logs.

      Now i have sixty thousand tasks ten thousand for each thread - six threads- after executing each task thread is calling post messages. but because of these post messages my application becomes very busy and looks like it hanged.

      if i remove post messages...every thing works fine. But i cannot call the procedure directly because it uses ui controls and ui controls are not thread safe and calling procedure directly from thread will lead to other errors.

      so is there any alternative available for postmessage and send message.

      Thanks, bASIL

      解决方案

      The problem is that there are two message queues for posted messages. The result of this is that your posted messages are always processed before any Paint, Input, or Timer messages.

      This means that you are flooding the message queue with a few hundred thousand messages. These messages will always get processed before paint and user messages - making your application appear to hang.

      The common way to solve this is to use a timer; have your code start a very short duration (e.g. 0 ms) timer.

      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).

      When timers fire they send a WM_TIMER message. This message will always be handled after any input and paint messages; making your application appear responsive.

      The other upside of setting a timer is that it forces you to "queue up" all the items you have processed in a (thread safe) list. You combine a few thousand work items into one "timer", saving the system from having to generate and process thousands of needless messages.

      Bonus Chatter

      ...messages are processed in the following order:

      • Sent messages
      • Posted messages
      • Input (hardware) messages and system internal events
      • Sent messages (again)
      • WM_PAINT messages
      • WM_TIMER messages

      Update: You should not have a timer polling, that's just wasteful and wrong. Your threads should "set a flag" (i.e. the timer). This allows your main thread to actually go idle, only waking when there is something to do.

      Update Two:

      Pseudo-code that demonstrates that order of message generation is not preserved:

      //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;
      

      Even though i generate a WM_ProcessNextItem after i generate a WM_PAINT message (i.e. Invalidate), the WM_PAINT will never get processed, because there is always another posted message before it. And as MSDN says, paint messages will only appear if there are no other posted messages.

      Update Three: Yes there is only message queue, but here's why we don't care:

      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:

      What I'll call them            Standard terminology
      ===========================    =============================
      Incoming sent messages         Non-queued messages
      Posted messages            \_  
      Input messages             /   Queued messages
      

      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."

      The Old New Thing, Practical Development Throughout the Evolution of Windows
      by Raymond Chen
      ISBN 0-321-44030-7
      Copyright © 2007 Pearson Education, Inc.
      Chapter 15 - How Window Messages Are Delivered and Retrieved, Page 358

      It's easier to imagine there are two message queues. No messages in the "second" one will be read until the "first" queue is empty; and the OP is never letting the first queue drain. As a result none of the "paint" and "input" messages in the second queue are getting processed, making the application appear to hang.

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

      Update Four

      The problem isn't necessarily that you are "flooding" the input queue with your messages. Your application can be unresponsive with only one message. As long as you have one posted message in the queue, it will be processed before any other message.

      Imagine a series of events happen:

      • Mouse is moved (WM_MOUSEMOVE)
      • Mouse left button is pushed down (WM_LBUTTONDOWN)
      • Mouse left button is released (WM_LBUTTONUP)
      • The user moves another window out of the way, causing your app to need to repaint (WM_PAINT)
      • Your thread has an item ready, and Posts a notification (WM_ProcessNextItem)

      Your application's main message loop (which calls GetMessage) will not receive messages in the order in which they happened. It will retrieve the WM_ProcessNextItem message. This removes the message from the queue, leaving:

      • WM_MOUSEMOVE
      • WM_LBUTTONDOWN
      • WM_LBUTTONUP
      • WM_PAINT

      While you process your item the user moves the mouse some more, and clicks randomly:

      • 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

      In response to your WM_ProcessNextItem you post another message back to yourself. You do this because you want to process the outstanding messages first, before continuing to process more items. This will add another posted message to the queue:

      • 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

      The problem starts to become apparent. The next call to GetMessage will retrieve WM_ProcessNextItem, leaving the application with a backlog of paint and input messages:

      • 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

      The solution is to take advantage of the out-of-order processing of messages. Posted messages are always processed before Paint/Input/Timer messages: don't use a posted message. You can think of the message queue as being divided into two groups: Posted messages and Input messages. Rather than causing the situation where the "Posted" messages queue is never allowed to empty:

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

      You an use a WM_TIMER message:

      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.

      It's important to note that you're not polling with a timer, that would be bad™. Instead you're firing a one-shot timer as a mechanism to generate a WM_TIMER message. You're doing this because you know that timer messages will not take priority over paint or input messages.

      Using a timer has another usability advantage. Between WM_PAINT, WM_TIMER, and input messages, there is out-of-order processing to them as well:

      • input messages, then
      • WM_PAINT, then
      • WM_TIMER

      If you use a timer to notify your main thread, you can also be guaranteed that you will process paint and user input sooner. This ensures that your application remains responsive. It's a usability enhancement and you get it for free.

      这篇关于替代邮箱和邮寄的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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