Postmessage 和 sendmessage 的替代方案

2024-04-21

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

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

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

那么是否有任何替代方案可用于发布消息和发送消息。

谢谢, 罗勒


问题是有两个用于发布消息的消息队列。这样做的结果是your发布的消息是总是在任何之前处理Paint, Input, or Timer消息。 http://msdn.microsoft.com/en-us/library/windows/desktop/ms644943%28v=vs.85%29.aspx

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

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

澄清

定时器消息(WM_TIMER),如画图消息(WM_PAINT)和输入消息(例如 WM_MOUSEMOVE、WM_KEYDOWN)被处理after发布消息。定时器消息专门在输入和绘制消息之后进行处理。

这意味着您的应用程序将响应用户事件和绘制请求before它处理下一个WM_TIMER信息。您可以通过使用计时器(它是WM_TIMER消息),而不是自己发布消息(这始终优先于绘制和输入消息)。

当定时器触发时,他们会发送一个WM_TIMER信息。该消息将始终被处理after任何输入和绘制消息;使您的应用程序看起来响应迅速。

设置计时器的另一个好处是,它强制您将已处理的所有项目“排队”到(线程安全)列表中。您可以将数千个工作项合并到一个“计时器”中,从而使系统不必生成和处理数千条不必要的消息。

奖金喋喋不休

...消息按以下顺序处理:

  • 发送信息
  • 发布的消息
  • 输入(硬件)消息和系统内部事件
  • 已发送消息(再次)
  • WM_PAINT http://msdn.microsoft.com/en-us/library/windows/desktop/dd145213%28v=vs.85%29.aspx消息
  • WM_TIMER http://msdn.microsoft.com/en-us/library/windows/desktop/ms644902%28v=vs.85%29.aspx消息

Update: 你应该not有一个计时器轮询,这只是浪费而且是错误的。你的线程应该“设置一个标志“(即计时器)。这允许您的主线程实际上处于空闲状态,仅在有事情要做时才唤醒。

更新二:

演示消息生成顺序未保留的伪代码:

//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 after我生成一个WM_PAINT消息(即Invalidate), the WM_PAINT永远不会被处理,因为有always另一个人在它之前发布了消息。和正如 MSDN 所说 http://msdn.microsoft.com/en-us/library/windows/desktop/ms644943%28v=vs.85%29.aspx,只有在没有其他发布的消息时才会出现绘制消息。

更新三:是的,只有消息队列,但这就是我们不关心的原因:

已发送和发布的消息

我在这里使用的术语是非标准的,但我使用它是因为我认为它比标准术语更清晰一些。出于本次讨论的目的,我将说与线程关联的消息分为三个桶,而不是更标准的两个:

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

实际上,消息分解比这更复杂,但我们现在将坚持上述模型,因为它“足够真实”。

旧事新事,Windows 演进过程中的实际开发
作者:雷蒙德·陈
国际标准书号 0-321-44030-7
版权所有 © 2007 培生教育公司
第 15 章 - 如何传递和检索窗口消息,第 358 页

更容易想象有two消息队列。在“第一个”队列为空之前,不会读取“第二个”队列中的消息;并且OP永远不会让第一个队列耗尽。因此,第二个队列中的“paint”和“input”消息都没有得到处理,使得应用程序看起来挂起。

这是实际情况的简化,但对于本次讨论的目的来说已经足够接近了。

更新四

问题不一定是您用消息“淹没”了输入队列。您的应用程序可能会无响应,仅one信息。只要你有one在队列中发布消息,它将在任何其他消息之前被处理。

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

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

您的应用程序的主消息循环 http://msdn.microsoft.com/en-us/library/windows/desktop/ms644928%28v=vs.85%29.aspx(这称为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

解决方案是利用消息的无序处理。发布的消息始终在绘制/输入/计时器消息之前处理:不要使用发布的消息。你可以think消息队列分为两组:发布消息和输入消息。而不是导致“已发布”消息队列永远不允许为空的情况:

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

挑剔角:这个关于两个队列的描述严格来说并不正确,但它足够真实。 Windows 如何按照记录的顺序传递消息是内部实现细节,可能随时更改。

重要的是要注意,你不是polling如果有计时器,那就糟糕了™。相反,你是firing一次性计时器作为一种机制来生成WM_TIMER信息。你这样做是因为你知道timer消息不会优先于paint or input消息。

使用计时器还有另一个可用性优势。之间WM_PAINT, WM_TIMER, and input消息,也有乱序处理:

  • input消息,然后
  • WM_PAINT, then
  • WM_TIMER

如果您使用计时器来通知主线程,还可以保证您将更快地处理绘制和用户输入。这可确保您的应用程序保持响应。这是可用性的增强,并且您可以免费获得。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Postmessage 和 sendmessage 的替代方案 的相关文章

  • TEdit onclick 全选?

    每当用户单击 TEdit1 或单击选择其中的某些文本时 如何选择 TEdit1 的所有文本 执行超出默认行为的任何操作都可能非常危险TEdit控制 您的用户知道标准 Windows 控件的行为方式 任何偏离此的行为都可能导致混乱 默认情况下
  • 如何将接口类型传递给过程

    如何将接口类型传递给过程参数 type Hello PortType interface ISoapInvokable 243CBD89 8766 F19D 38DF 427D7A02EAEE function GetDeneme s st
  • iOS Objective-C 对象:何时使用release,何时不使用它

    我在 iOS 下 正在使用 delphi Tokyo 进行开发 这是我的代码 aUIImage TUIImage Wrap TUIImage alloc initWithCGImage aCGImageRef try aData TNSDa
  • delphi分组框标题颜色变化

    我正在使用 BDS 2006 想知道您是否可以使用项目中存在的 XPmanifest 更改组框和单选按钮组标题的颜色 因为它始终是蓝色 唯一的方法是重写 Paint 方法TGroupBox http docwiki embarcadero
  • 为应用程序启用主题

    我有一个旧的应用程序 在Win XP中的delphi 7中启动 现在我正在使用delphi 2009 win Vista 如果我开始一个新项目 所有按钮都有一个圆形边缘 但在我的旧应用程序中 所有按钮都有 方形 形状的外观 有什么设置我错过
  • Delphi - 相当于C#的三元运算符? [复制]

    这个问题在这里已经有答案了 可能的重复 Delphi 中是否存在或者将来是否存在条件运算符 https stackoverflow com questions 2108609 is there or is there ever going
  • Delphi - 将物理路径(设备文件句柄)转换为虚拟路径

    我怎样才能转换像这样的路径 设备 HarddiskVolume3 Windows 进入其相应的虚拟路径 如本例中的 c Windows 我个人更喜欢原生方式 function GetHDDDevicesWithDOSPath TString
  • 如何将 TGifImage 中的帧提取为位图?

    下面的演示尝试在表单的画布上绘制 GIF 这不起作用 图像不会前进 如何让它发挥作用 procedure TForm1 FormCreate Sender TObject begin GIF TGIFImage Create GIF Loa
  • 以 png 格式剪辑幻灯片 (Delphi 2010)

    I have a filmstrip of images in png format like this 我想知道如何剪辑每个图像并将这些图像放入 TImageList 控件中 并始终保留透明度 EDIT 是的 在设计时 RRUZ 提到的技
  • Delphi中的抽象类

    我正在使用一个具有许多抽象类的组件套件 现在我想应用多态性 但在创建对象时收到错误抽象类 即使我不需要 我是否应该重写所有虚拟方法 有什么解决方法或解决方案吗 为了创建类的实例 您需要重写所有声明为虚拟抽象的方法 即使您不使用它们 如果您确
  • 在 Delphi 2009 上安装最新版本的 Indy 10 [重复]

    这个问题在这里已经有答案了 是否有更新 Delphi 2009 中的 Indy 10 组件的分步指南 我读过正在卸载线程 https stackoverflow com questions 486210 what is the proper
  • 如何向标准集合编辑器添加图标?

    我有一个自定义控件 它利用TCollection and TCollectionItem 在集合编辑器中 我想向每个列表项添加图标 该列表项由内部TImageList 在其父组件内 集合项本身代表图标 我想在这个编辑器中显示相应的图标 如何
  • DELPHI 和 WANT 或 NANT

    We use 巡航控制 net http confluence public thoughtworks org display CCNET Welcome to CruiseControl NET在 Delphi 2006 应用程序中进行持
  • Delphi - 如何获取 USB 可移动硬盘和记忆棒的列表?

    在我的应用程序 Delphi 中 我需要列出所有 USB 存储设备 这些可以是闪存棒or外部存储驱动器 有一个Jvcl成分JvDriveCombo 并且它有DriveType属性 问题是我是否选择DriveType Fixed那么除了外部驱
  • 如何从 Delphi 中的函数返回对象而不导致访问冲突?

    我有一个返回 TStringList 的 delphi 函数 但是当我返回一个值并尝试使用它时 我收到一个访问冲突错误 即 myStringList FuncStringList myStringList Items Count lt Th
  • TColorProperty德尔福柏林10.1.2?

    我正在尝试将组件从 Delphi 7 转换为 Delphi Berlin 平面组件 https sourceforge net projects flatstyle https sourceforge net projects flatst
  • Delphi 流畅的界面

    使用上有什么优点和缺点流畅的界面 http en wikipedia org wiki Fluent interface在德尔福 流畅的界面应该会增加可读性 但我对此有点怀疑one包含很多链式方法的长 LOC 是否存在编译器问题 是否存在任
  • 如何使用 Gmail 的 SMTP 和 Indy 10 发送电子邮件?

    我正在使用 Delphi 2009 和 svn 中最新的 Indy 10 通过 SMTP 发送电子邮件 但它不适用于 Gmail Google Apps 托管域 当我尝试发送电子邮件时 我收到 必须首先发出 STARTTLS 命令 我尝试用
  • 如何用不同的颜色绘制选定的列表框项目?

    是否可以更改 TListBox 中的项目选择焦点颜色和文本颜色 当项目中未启用主题或列表框样式设置为所有者绘制时 项目周围的选择将被涂成蓝色 我相信这是由系统的外观设置全局定义的 我想将所选项目的颜色更改为自定义颜色 举个例子 结果会是这样
  • 从 Delphi VCL 样式获取特定字形

    我想从 VCL 样式获取特定的位图 并将其设置为按钮上的图像 它实际上是帮助问号 在位图样式编辑器中是来自表单的 btnHelp 图像 要从 VCL 样式获取视觉元素 字形 您必须使用GetElementDetails和TCustomSty

随机推荐

  • 如何在 JavaScript 中管理“未捕获的异常”以便在用户界面中显示错误消息?

    当某些网站或 Web 应用程序中抛出未捕获的异常时 每个浏览器的开发者工具中都会出现错误 In Electron https github com electron electron issues 2479 issuecomment 130
  • 将 li 放在特定高度之后的下一列中

    我正在 WordPress 网站上开发导航菜单 请看一下背景图像始终与顶部对齐 https stackoverflow com questions 12456428 background image always align to top对
  • 如何在 Linux 中使用它们列出活动端口和进程,C 代码

    我正在尝试编写 C 代码来完成与以下相同的工作 netstat vatp 列出所有远程 本地地址和使用它们的进程 但我不知道我应该阅读哪些文件 我尝试调查 proc net tcp and proc net udp 但它们没有进程名称或进程
  • 如何通过过滤计算 Vaadin 8 网格页脚中的总计

    我知道我必须使用grid getDataProvider 得到ListDataProvider 假设我发送了List to grid setItems 在其他计算页脚总数中我这样做 Collection myItems ListDataPr
  • 从另一个域重定向时 Django 会话被丢弃

    当用户访问我的域时 django 会发出一个 sessionid 当他尝试使用 Facebook 进行 Oauth 时 他单击我网站上的一个按钮 该按钮会重定向到 Facebook com Facebook 重定向回我的域 但此时 用户的会
  • 在 UIImageView 上添加不透明度为 0.3 的黑色覆盖层

    我有一个 UIImageView 我想在它上面添加一个黑色覆盖层 在不必重写drawRect的情况下执行此操作的最佳方法是什么 我正在考虑在其上添加一个 CALayer 但不确定如何获得具有 0 3 alpha 的黑色 CALayer 像这
  • EF Code First 现有数据库

    首先使用 EF 4 1 代码我想停止 EF 从模型创建数据库 据我了解 如果您传递 Connectionstring 名称 它将使用该连接字符串中的现有数据库 但是 如果数据库不存在 那么它将创建数据库 将所有表形成模型
  • MYSQL DATE 函数在 LEFT JOIN 中运行速度极慢

    添加行时 LEFT JOIN core records sales as sales ON DATE appointments date DATE sales date sold 根据我的查询 它将脚本的运行时间从大约 8 秒增加到 2 3
  • 如何在manim中一个接一个地应用两个变换?

    我想申请两个线性变换矩阵一个接一个地在manimce 以下是一次转换的代码 from manim import class LT LinearTransformationScene def init self LinearTransform
  • 回调函数含义

    javascript中的回调函数是什么意思 JavaScript 的 回调 是函数对象 可以传递给其他函数 例如函数指针或委托函数 然后在函数完成时或需要时调用 例如 您可以有一个主函数 您可以向该函数传递一个它将调用的函数 主要功能可以如
  • Android Studio升级到Arctic Fox后出现奇怪的代码子窗口(2020.3.1)

    在 Android Studio 升级到 Arctic Fox 版本后 我的代码编辑器中现在出现了这些奇怪的子窗口 但我无法摆脱它们 如果我单击 2 个子窗口中的任何一个 顶部的单行窗口或下面的 5 行窗口 见下图 它会滚动到有问题的代码
  • Laravel 路由使用 nginx 覆盖 phpmyadmin 路径

    我的 LEMP Droplet 上有以下 nginx 配置 server listen 80 default server listen 80 default server ipv6only on root var www html pub
  • 在 C 中为 fgets 创建超时[重复]

    这个问题在这里已经有答案了 我的目的是创建一个 tfgets 函数 tfgets 与 fget 类似 只是它的超时时间为 1 秒 如果 1 秒内没有收到输入 则程序返回 NULL 否则 它将返回 fgets 返回的任何内容 如何为 tfge
  • Gson: [Class] 声明多个名为 [property] 的 JSON 字段

    我正在尝试使用以下 POJO 序列化为 JSONGson https github com google gson public class Member private long id private long customerAccou
  • Pytest 适用于旧的模拟,但不适用于 unittest.mock

    我正在将一些代码从 Python 2 移植到 3 并且py test玩得不好patch装饰器来自unittest mock 当我使用patch装饰器将模拟传递到测试函数的参数中 py test相反 将该参数解释为固定装置 并且无法设置测试
  • 如何在使用用户限制资源访问保护的 python eve api 中创建新用户帐户

    我首先使用 python eve 框架创建了一个 Web api 无需身份验证或用户帐户 效果非常好 我现在正在尝试添加身份验证和用户帐户 但遇到了一些困难 我想使用用户限制的资源访问 但是如果资源受到限制 用户如何创建新的用户帐户 我缺少
  • 如何从数据库中删除字段?

    当我单击该图标时 所单击的新闻字段将添加到数据库中 final fireStore FirebaseFirestore instance IconButton onPressed async newsController addNews a
  • 区分大小写的 SQL 区分大小写

    我正在尝试请求一个区分大小写的结果 例如在我的数据库中我有 ABCdef abcDEF abcdef 请求是 SELECT FROM table WHERE col abcdef 但我有 3 行结果 我只想要 abcdef 我尝试找到解决方
  • 如何获取 Kendo DropDownList 的选定值

    我不知道如何确定在我的剑道下拉列表中选择了哪个项目 我的观点将其模型定义为 model KendoApp Models SelectorViewModel ViewModel 定义为 public class SelectorViewMod
  • Postmessage 和 sendmessage 的替代方案

    我有一个程序 它使用多个线程来执行某些任务 每个线程都有一堆任务要执行 执行其中之一后 每个线程都会向主屏幕调用一条发布消息来更新日志 现在我有六万个任务 每个线程一万个 六个线程 执行每个任务线程后调用发布消息 但由于这些帖子消息 我的应