不使用 SendMessage() 从其他线程访问 UI 控件的原因?
Reasons for not using SendMessage() to access UI controls from other threads?
我读到 SendMessage()
不应该用于从其他线程访问 UI 控件,但我不确定我知道为什么,我能想到的唯一原因是因为SendMessage()
是阻塞调用,在某些情况下可能会导致死锁。
但这是不使用它的唯一原因吗?
编辑: 这个article讲了不使用SendMessage()
的原因但是我觉得不是很清楚(有意对于 .NET)。
最好记住,您编写正确代码的几率不是很高。一般的建议是不要这样做! 永远 没有必要,GUI 的UI 线程] Windows 中的程序的结构完全是为了让运行在另一个线程上或进程内部的代码能够简单地影响程序的 UI。消息循环的要点,万能的解决办法producer-consumer problem。 PostMessage() 是您利用它的武器。
无论如何,在您继续前进之前,请先考虑一个使用 SendMessage 时很难解决的简单问题。如何安全正确地关闭window?
假设您需要关闭window的确切时间是完全不可预测的,并且与工作线程的执行完全不同步。是用户关闭它,或者要求UI线程终止,你需要确保线程已经退出并停止调用SendMessage,然后才能真正关闭window.
执行此操作的直观方法是在 WM_CLOSE 消息处理程序中发出事件信号,要求线程停止。并等待它完成,然后 window 可以关闭。直观,但它不起作用,它会使你的程序死锁。有时,并非总是如此,很难调试。当线程无法检查事件时出错,因为它卡在 SendMessage 调用中。由于 UI 线程正在等待线程退出,因此无法完成。工作线程无法继续,UI 线程也无法继续。一个"deadly embrace",你的程序会挂掉,需要强行杀掉。死锁是一个标准的线程错误。
你会大喊,"I'll use SendMessageTimeout!" 但是你为 uTimeout 参数传递了什么?你如何解释 ERROR_TIMEOUT 错误? UI 线程在一段时间内出现紧张症是很常见的,您肯定已经看过 "ghost window" 之前,标题栏中显示“无响应”的线程。因此 ERROR_TIMEOUT 不能可靠地指示 UI 线程正在尝试关闭,除非您使 uTimeout 非常大。至少 10 秒。这有点管用,但偶尔在退出时挂起 10 秒并不是很好。
解决所有消息的此类问题,而不仅仅是WM_CLOSE。 WM_PAINT 应该是下一个,另一个非常非常难以完全解决的问题。您的工作线程要求在 UI 线程调用 EndPaint() 之前一毫秒更新显示。因此永远不会显示更新,它只会丢失。线程竞赛,另一个标准线程错误。
第三个经典的线程错误是 fire-hose 问题。当您的工作线程产生结果的速度快于 UI 线程处理它们的速度时,就会发生这种情况。很常见,UI 更新非常昂贵。容易检测,很难解决,而且发生时无法预测。易于检测,因为您的 UI 会冻结,UI 线程会消耗 100% 的核心以跟上消息速率。它不再处理 low-priority 任务。喜欢绘画。使用 SendMessage 或 PostMessage 时出错 both。在后一种情况下,您需要将消息 queue 填满。它在包含 10000 条未处理的消息后开始失败。
长话短说,是的,SendMessage() 是 thread-safe。但是 thread-safety 不是可传递的 属性,它不会自动生成您自己的代码 thread-safe。当您使用线程时,您仍然会遇到 所有 可能出错的问题。死锁、竞争、fire-hosing。害怕穿线兽。
我读到 SendMessage()
不应该用于从其他线程访问 UI 控件,但我不确定我知道为什么,我能想到的唯一原因是因为SendMessage()
是阻塞调用,在某些情况下可能会导致死锁。
但这是不使用它的唯一原因吗?
编辑: 这个article讲了不使用SendMessage()
的原因但是我觉得不是很清楚(有意对于 .NET)。
最好记住,您编写正确代码的几率不是很高。一般的建议是不要这样做! 永远 没有必要,GUI 的UI 线程] Windows 中的程序的结构完全是为了让运行在另一个线程上或进程内部的代码能够简单地影响程序的 UI。消息循环的要点,万能的解决办法producer-consumer problem。 PostMessage() 是您利用它的武器。
无论如何,在您继续前进之前,请先考虑一个使用 SendMessage 时很难解决的简单问题。如何安全正确地关闭window?
假设您需要关闭window的确切时间是完全不可预测的,并且与工作线程的执行完全不同步。是用户关闭它,或者要求UI线程终止,你需要确保线程已经退出并停止调用SendMessage,然后才能真正关闭window.
执行此操作的直观方法是在 WM_CLOSE 消息处理程序中发出事件信号,要求线程停止。并等待它完成,然后 window 可以关闭。直观,但它不起作用,它会使你的程序死锁。有时,并非总是如此,很难调试。当线程无法检查事件时出错,因为它卡在 SendMessage 调用中。由于 UI 线程正在等待线程退出,因此无法完成。工作线程无法继续,UI 线程也无法继续。一个"deadly embrace",你的程序会挂掉,需要强行杀掉。死锁是一个标准的线程错误。
你会大喊,"I'll use SendMessageTimeout!" 但是你为 uTimeout 参数传递了什么?你如何解释 ERROR_TIMEOUT 错误? UI 线程在一段时间内出现紧张症是很常见的,您肯定已经看过 "ghost window" 之前,标题栏中显示“无响应”的线程。因此 ERROR_TIMEOUT 不能可靠地指示 UI 线程正在尝试关闭,除非您使 uTimeout 非常大。至少 10 秒。这有点管用,但偶尔在退出时挂起 10 秒并不是很好。
解决所有消息的此类问题,而不仅仅是WM_CLOSE。 WM_PAINT 应该是下一个,另一个非常非常难以完全解决的问题。您的工作线程要求在 UI 线程调用 EndPaint() 之前一毫秒更新显示。因此永远不会显示更新,它只会丢失。线程竞赛,另一个标准线程错误。
第三个经典的线程错误是 fire-hose 问题。当您的工作线程产生结果的速度快于 UI 线程处理它们的速度时,就会发生这种情况。很常见,UI 更新非常昂贵。容易检测,很难解决,而且发生时无法预测。易于检测,因为您的 UI 会冻结,UI 线程会消耗 100% 的核心以跟上消息速率。它不再处理 low-priority 任务。喜欢绘画。使用 SendMessage 或 PostMessage 时出错 both。在后一种情况下,您需要将消息 queue 填满。它在包含 10000 条未处理的消息后开始失败。
长话短说,是的,SendMessage() 是 thread-safe。但是 thread-safety 不是可传递的 属性,它不会自动生成您自己的代码 thread-safe。当您使用线程时,您仍然会遇到 所有 可能出错的问题。死锁、竞争、fire-hosing。害怕穿线兽。