【问题标题】:Reasons for not using SendMessage() to access UI controls from other threads?不使用 SendMessage() 从其他线程访问 UI 控件的原因?
【发布时间】:2015-04-11 16:23:17
【问题描述】:

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

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


编辑:这个article 谈到了不使用SendMessage() 的原因,但我觉得不是很清楚(它是为.NET 设计的)。

【问题讨论】:

  • 您是否考虑过您阅读的内容可能是糟糕的建议,或者您误解了您阅读的内容?在很多情况下SendMessage 是合适的,也有很多情况是不合适的。我们不可能把它们都一一列举。您需要更加关注您的问题。
  • @David Heffernan 我读过的大部分教程都是由看似可靠的作者编写的,例如:flounder.com/workerthreads.htm,请参阅部分:工作线程和 GUI II:不要触摸 GUI.
  • 有些东西是安全的,有些则不是。您需要了解细节,而不是遵循一揽子规则。
  • @John 我怀疑有这样的事情。你只需要了解规则。
  • 好吧,如果你不是很小心的话,又是一场经典的线程竞赛。当用户同时忙于修改文本内容时,你期望得到什么?

标签: multithreading winapi


【解决方案1】:

最好记住,您编写正确代码的几率不是很高。一般的建议是不要这样做!从不有必要,Windows 中 GUI 程序的 UI 线程完全是结构化的,可以让代码变得简单在另一个线程或进程内部运行会影响程序的 UI。消息循环的要点,producer-consumer problem的通用解决方案。 PostMessage() 是你利用它的武器。

在您继续前进之前,请先考虑一个使用 SendMessage 时很难解决的简单问题。如何安全正确地关闭窗户?

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

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

您会大喊:“我将使用 SendMessageTimeout!”但是你为 uTimeout 参数传递了什么,你如何解释一个 ERROR_TIMEOUT 错误? UI 线程一段时间内紧张是很常见的,您肯定之前见过“幽灵窗口”,即在标题栏中显示“未响应”的窗口。因此 ERROR_TIMEOUT 并不能可靠地表明 UI 线程正在尝试关闭,除非您将 uTimeout 设置得非常大。至少 10 秒。这有点工作,但偶尔在出口处挂起 10 秒并不是很漂亮。

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

第三个经典的线程错误是 fire-hose 问题。当您的工作线程产生结果的速度快于 UI 线程可以处理它们时,就会发生这种情况。很常见,UI 更新非常昂贵。易于检测,很难解决,并且在发生时无法预测。易于检测,因为您的 UI 将冻结,UI 线程会消耗 100% 的内核以跟上消息速率。它不再处理它的低优先级任务。就像绘画一样。当您使用 SendMessage 或 PostMessage 时出错两者。在后一种情况下,您将把消息队列填满。它在包含 10000 条未处理的消息后开始失败。

长话短说,是的,SendMessage() 是线程安全的。但是线程安全不是传递属性,它不会自动使您自己的代码成为线程安全的。 所有当你使用线程时,你仍然会遇到可能出错的事情。死锁,比赛,灭火。害怕穿线的野兽。

【讨论】:

  • 所以最好不要使用SendMessage()。但是PostMessage()是异步的,怎么能用来获取一个UI控件的内容呢!
  • 你不应该写这样的代码,这是另一个线程竞争错误。在启动工作线程之前从 UI 收集信息。禁用窗口,使用户无法更改文本并知道无法进行编辑。工作人员完成后重新启用窗口。
  • 我的工作线程在应用程序终止之前不会终止。它必须经常从 UI 控件获取数据以及向 UI 控件显示数据。
  • 这个信息现在应该很清楚了:你做错了。以自己的方式解决您正在处理的问题,这完全取决于您自己,没有通用的解决方案。这并非不可能,您必须单击按钮详细描述您的具体问题。
  • @user4582812 您提出了一个广泛而普遍的问题。并得到了相同性质的答案。接下来,您开始询问您的应用程序的细节,只有您知道。如果您想询问具体问题,请这样做,但您提出一般性问题,然后针对您的具体问题提出 cmets 似乎很奇怪。您在第一条评论中得出的结论是完全错误的。世界不会分为调用SendMessage 解决的问题和调用PostMessage 解决的问题。这是一种错误的二分法。
猜你喜欢
  • 1970-01-01
  • 2012-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多