gpt4 book ai didi

delphi - Postmessage 和 sendmessage 的替代方案

转载 作者:行者123 更新时间:2023-12-03 14:37:25 32 4
gpt4 key购买 nike

我有一个程序,它使用多个线程来执行某些任务。每个线程都有一堆任务要执行。执行其中之一后,每个线程都会向主屏幕调用一个发布消息以更新日志。

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

如果我删除帖子消息...一切正常。但是我不能直接调用该过程,因为它使用 ui 控件并且 ui 控件不是线程安全的,直接从线程调用过程会导致其他错误。

那么是否有任何替代方法可用于 postmessage 和 send message。

谢谢,
罗勒

最佳答案

问题是发布的消息有两个消息队列。这样做的结果是您发布的消息是 always processed before any Paint , Input , or Timer messages.

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

解决这个问题的常用方法是使用定时器;让您的代码启动一个非常短的持续时间(例如 0 毫秒)计时器。

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)
  • WM_PAINT messages
  • WM_TIMER 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, 0);
end;
else
DefWindowProc(g_dc, Message.Msg, Message.wParam, Message.lParam);
end;
end;

即使我生成了 WM_ProcessNextItem在我生成 WM_PAINT 之后消息(即 Invalidate ), 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:

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



旧的新事物,贯穿Windows演进的实用发展
by Raymond Chen
ISBN 0-321-44030-7
版权所有 © 2007 Pearson Education, Inc.
第 15 章 - 如何传递和检索窗口消息,第 358 页

更容易想象有两个消息队列。在“第一个”队列为空之前,不会读取“第二个”队列中的任何消息;并且 OP 永远不会让第一个队列耗尽。结果,第二个队列中的“绘制”和“输入”消息都没有得到处理,使应用程序看起来挂起。

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

  • 解决方案是利用消息的乱序处理。发布的消息总是在 Paint/Input/Timer 消息之前处理:不要使用发布的消息。您可以将消息队列分为两组:已发布消息和输入消息。而不是导致永远不允许“已发布”消息队列为空的情况:
    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 - Postmessage 和 sendmessage 的替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7616591/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com