在线程中更改GUI

时间:2014-01-29 13:11:13

标签: c++ multithreading mfc thread-safety sendmessage

我的操作大约在20秒内结束。为了避免冻结,我想创建一个线程并每秒更新一个标签文本。我搜索了很多,因为每个人都有不同的意见,我无法决定使用哪种方法。

我尝试使用SendMessage并且它有效,但有些人认为使用SendMessage并不安全,我应该使用PostMessage。但PostMessage失败了ERROR_MESSAGE_SYNC_ONLY(1159)。

char text[20] = "test text";
SendMessage(label_hwnd, WM_SETTEXT, NULL, text);

我搜索了这个,我认为这是因为在PostMessage中使用指针是不允许的。这就是它失败的原因。

那么,我该怎么办?我糊涂了。你有什么建议?这种方法是否适合更改其他线程中的UI元素?

由于

6 个答案:

答案 0 :(得分:4)

嗯,错误说明了一切。消息无法异步发送。关于PostMessage的事情是它将消息发布到侦听线程的队列并立即返回,而不等待消息处理的结果。另一方面,SendMessage等待窗口过程完成处理消息,然后才返回。

在您的情况下使用PostMessage的风险是在窗口过程处理之前您可能已释放字符串缓冲区的消息。因此,在这个实例中使用SendMessage更安全,这就是MS开发人员在决定不允许异步发布此特定消息时可能会想到的。

编辑:为了清楚起见,当然这并没有消除完全传递裸指针的风险。

答案 1 :(得分:4)

ERROR_MESSAGE_SYNC_ONLY的文档说:

  

该消息只能用于同步操作。

这意味着您可以使用同步邮件传递,即SendMessage等,但您不能使用异步邮件传递,即PostMessage

原因是WM_SETTEXT是一个参数包含引用的消息。参数不能按值复制。如果您可以异步传递WM_SETTEXT,那么系统如何保证收件人窗口收到的指针仍然有效?

因此,系统只是拒绝您发送此消息的尝试,实际上拒绝任何其他具有参考参数的消息。

在此处使用SendMessage是合理的。这肯定会奏效。

但是,您强制您的工作线程在UI上阻止。 UI可能需要一些时间来更新标题的文本。另一种方法是将自定义消息发布到UI线程,指示UI线程更新UI。然后,您的工作线程线程可以继续其任务并让UI线程并行更新,而不会阻塞工作线程。

为了使其工作,您需要一种方法让UI线程从工作线程获取进度信息。如果进度像百分比一样简单,那么您需要做的就是让工作线程写入,并从共享变量读取UI线程。

答案 2 :(得分:3)

asynch PostMessage()替代方案要求参数中传递的数据的生命周期扩展到消息创建者函数之外。这样做的“经典”方法是堆分配数据,PostMessage是指向它的指针,以通常的方式处理消息处理程序中的数据,然后将其删除,(或以其他方式处理它,以便它不泄漏)。换句话说,“发射并忘记” - 在PostMessage发布后,您不得触摸原始线程中的数据。

优点是PostMessage()允许原始线程“立即”运行,因此可以进一步工作(可能发布更多消息)。如果GUI忙,可以阻止SendMessage()和此类同步通信,从而影响整体吞吐量。

缺点是线程可能比GUI处理它们更快地生成消息。这通常表现为滞后的GUI响应,特别是在执行GUI-intenisve工作时,如移动/调整窗口大小和更新TreeViews。最终,当10,000多条消息排队时,PostMessage调用将失败。如果发现这是一个问题,可能必须添加额外的流量控制,因此进一步使通信复杂化(我通常通过使用固定大小的对象池来阻止/限制原始线程,如果所有可用对象都是在发布的但未处理的消息中卡住了“过境”。

答案 3 :(得分:2)

来自MSDN

如果将WM_USER下面的消息发送到异步消息函数(PostMessage,SendNotifyMessage和SendMessageCallback),则其消息参数不能包含指针。否则,操作将失败。

答案 4 :(得分:1)

我认为你可以在这里安全地使用SendMessage。然后,您不必担心字符串和其他问题的内存持久性。 当您从另一个消息处理程序发送消息或向已阻止的GUI线程发送消息时,SendMessage不安全,但如果在您的情况下您知道它是安全的 - 只需使用它

答案 5 :(得分:1)

这不是PostMessage的问题,但是您发送的邮件存在问题 - WM_SETTEXT。首先,一个常见的误解是,如果SendMessage()来自一个线程的控件,它与调用GUI API不同,它实际上是 NOT 。当你调用GUI API(从任何地方)例如设置文本时,windows以SendMessage()调用的形式实现它。因此,当您发送相同的消息时,它与调用API基本相同。虽然像这样的GUI直接访问在很多方面都有用,但不建议这样做。因此,我不同意@David接受的答案。

正确的方法是(动态代码)

char* text = new char[20] 
strcpy_s(text, "test text");
PostMessage(label_hwnd, IDM_MY_MSG_UPDATE_TEXT, NULL, text); 

您将更新自己的消息IDM_MY_MSG_UPDATE_TEXT处理函数中的文本并删除内存。

相关问题