为什么不使用SendMessage()从其他线程访问UI控件的原因? [英] Reasons for not using SendMessage() to access UI controls from other threads?

查看:87
本文介绍了为什么不使用SendMessage()从其他线程访问UI控件的原因?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读到SendMessage()不应用于从其他线程访问UI控件,但我不确定为什么,我能想到的唯一原因是因为SendMessage()是阻止调用,那么在某些情况下可能会导致死锁.

I have read that SendMessage() should not be used to access UI controls from other threads, but I'm not sure I know why, the only reason that I can think of is since SendMessage() is a blocking call, then it could cause a deadlock in certain situations.

但这是不使用它的唯一原因吗?

But is this the only reason not to use it?

:此文章讨论了不使用SendMessage()的原因,但我认为它不太清楚(它适用于.NET).

This article talks about the reasons not to use SendMessage() but I don't find it to be very clear (it is intended for .NET).

推荐答案

最好记住,编写正确代码的可能性不是很好.通用的建议是不要这样做! 永远是不必要的,Windows中GUI程序的UI线程是完全结构化的,以使其易于允许执行以下操作的代码:在另一个线程上运行或在进程内运行会影响程序的UI.消息循环的关键,是生产者-消费者问题. PostMessage()是您利用它的武器.

It is best to keep in mind that the odds that you will write correct code are not very good. And the generic advice is don't do it! It is never necessary, the UI thread of a GUI program in Windows was entirely structured to make it simple to allow code that runs on another thread or inside a process affect the UI of the program. The point of the message loop, the universal solution to the producer-consumer problem. PostMessage() is your weapon to take advantage of it.

无论如何,在继续前进之前,首先要考虑一个简单的问题,当您使用SendMessage时很难解决.您如何安全正确地关闭窗口?

Before you forge ahead anyway, start by thinking about a simple problem that's very hard to solve when you use SendMessage. How do you close a window safely and correctly?

考虑到您需要关闭窗口的确切时间完全是不可预测的,并且与工作线程的执行完全不同步.是用户关闭它,或要求UI线程终止,您需要确保该线程已退出并停止调用SendMessage,然后才能真正关闭窗口.

Given is that the exact moment in time that you need to close the window is entirely unpredictable and completely out of sync with the execution of your worker thread. It is the user that closes it, or asks the UI thread to terminate, you need to make sure that the thread has exited and stops calling SendMessage before you can actually close the window.

执行此操作的直观方法是在WM_CLOSE消息处理程序中向事件发出信号,要求线程停止.并等待它完成,然后窗口可以关闭.直观,但不起作用,它将使程序死锁.有时,并非总是如此,很难调试.当线程由于被卡在SendMessage调用中而无法检查事件时,将出错.由于UI线程正在等待线程退出,因此无法完成.工作线程无法继续,UI线程无法继续.一个致命的拥抱",您的程序将挂起,需要被强行杀死.死锁是标准的线程错误.

The intuitive way to do this is to signal an event in your WM_CLOSE message handler, asking the thread to stop. And wait for it to complete, then the window can close. Intuitive, but it does not work, it will deadlock your program. Sometimes, not always, very hard to debug. Goes wrong when the thread cannot check the event because it is stuck in the SendMessage call. Which cannot complete since the UI thread is waiting for the thread to exit. The worker thread cannot continue and the UI thread cannot continue. A "deadly embrace", your program will hang and needs to be killed forcibly. Deadlock is a standard threading bug.

您将大喊:我将使用SendMessageTimeout!"但是,您为 uTimeout 参数传递了什么,以及如何解释ERROR_TIMEOUT错误? UI线程出现故障是很常见的,肯定您之前已经看过幽灵窗口",即在标题栏中显示"Not Responding"的窗口.因此,除非您使uTimeout很大,否则ERROR_TIMEOUT不能可靠地指示UI线程正在尝试关闭.至少10秒.这种方法行得通,但是在出口处偶尔出现10秒的挂起并不是一件很漂亮的事情.

You'll shout, "I'll use SendMessageTimeout!" But what do you pass for the uTimeout argument and how do you interpret an ERROR_TIMEOUT error? It is pretty common for a UI thread to go catatonic for a while, surely you've seen the "ghost window" before, the one that shows 'Not Responding` in the title bar. So an ERROR_TIMEOUT does not reliably indicate that the UI thread is trying to shut down unless you make uTimeout very large. At least 10 seconds. That kinda works, getting the occasional 10 second hang at exit is however not very pretty.

解决所有消息的这种问题,而不仅仅是WM_CLOSE.接下来应该是WM_PAINT,这是非常非常难以解决的另一个问题.您的工作线程要求在UI线程调用EndPaint()之前的一毫秒内更新显示.因此,从不显示更新,它只会丢失.一场线程竞赛,另一个标准的线程错误.

Solve this kind of problem for all the messages, not just WM_CLOSE. WM_PAINT ought to be next, another one that's very, very hard to solve cleanly. Your worker thread asks to update the display a millisecond before the UI thread calls EndPaint(). And thus never displays the update, it simply gets lost. A threading race, another standard threading bug.

第三个经典的线程错误是 fire-hose 问题.当工作线程产生结果的速度快于UI线程处理结果的速度时,就会发生这种情况.非常普遍,UI更新非常昂贵.易于检测,很难解决,并且在发生时无法预测.由于您的UI会冻结,因此易于检测,UI线程消耗了100%的内核,试图跟上消息速率.它不再绕过低优先级的任务.喜欢画画.当您使用SendMessage或PostMessage时,都是.在后一种情况下,您将填满消息队列.包含10000条未处理的消息后,它开始失败.

The third classic threading bug is a fire-hose problem. Happens when your worker thread produces results faster than the UI thread can handle them. Very common, UI updates are pretty expensive. Easy to detect, very hard to solve and unpredictable when it occurs. Easy to detect because your UI will freeze, the UI thread burns 100% core trying to keep up with the message rate. It doesn't get around to its low-priority tasks anymore. Like painting. Goes wrong both when you use SendMessage or PostMessage. In the latter case you'll fill the message queue up to capacity. It starts failing after it contains 10000 unprocessed messages.

长话短说,是的,SendMessage()是线程安全的.但是线程安全性不是可传递的属性,它不会自动使您自己的代码成为线程安全的.您仍然遭受 all 的困扰,这在使用线程时可能会出错.僵局,种族,火灾.害怕穿线的野兽.

Long story short, yes, SendMessage() is thread-safe. But thread-safety is not a transitive property, it doesn't automatically make your own code thread-safe. You still suffer from all the things that can go wrong when you use threads. Deadlocks, races, fire-hosing. Fear the threading beast.

这篇关于为什么不使用SendMessage()从其他线程访问UI控件的原因?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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