在WinForms中,为什么不能从其他线程更新UI控件?

时间:2008-08-13 20:41:25

标签: winforms multithreading

我确信这有一个好的(或至少是体面的)原因。它是什么?

7 个答案:

答案 0 :(得分:24)

  

我认为这是一个很棒的问题 -   我认为需要更好的   答案。

     

当然唯一的原因就是那里   在某个框架中某种东西   这不是非常线程安全的。

在System.Windows.Forms的每个控件上,“something”几乎就是每个实例成员。

System.Windows.Forms中许多控件的MSDN文档,如果不是全部,请说“此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。不保证任何实例成员是线程安全的。“

这意味着TextBox.Text {get; set;}等实例成员不是 可重入

使每个实例成员都是线程安全的,可能会引入大量应用程序不需要的开销。相反,.Net框架的设计者决定,并且我认为正确的是,应该将来自多个线程的表单控件的访问同步的负担放在程序员身上。

[编辑]

虽然这个问题只是问“为什么”这里是指向解释“如何”的文章的链接:

如何:在MSDN上对Windows窗体控件进行线程安全调用

http://msdn.microsoft.com/en-us/library/ms171728.aspx

答案 1 :(得分:10)

因为您可以轻松地结束僵局(以及其他问题)。

例如,您的辅助线程可能正在尝试更新UI控件,但UI控件将等待由辅助线程锁定的资源被释放,因此两个线程最终都会等待彼此完成。正如其他人所评论的那样,这种情况并不是UI代码所特有的,但却特别常见。

在其他语言(如C ++)中,您可以自由尝试执行此操作(没有像在WinForms中那样抛出异常),但是如果发生死锁,您的应用程序可能会冻结并停止响应。

顺便提一下,您可以轻松告诉UI线程您要更新控件,只需创建一个委托,然后在该控件上调用(异步)BeginInvoke方法,将其传递给您的委托。 E.g。

myControl.BeginInvoke(myControl.UpdateFunction);

这相当于从工作线程

执行C ++ / MFC PostMessage

答案 2 :(得分:7)

虽然听起来合理,但约翰的回答并不正确。事实上即使使用Invoke,你仍然不安全,不会遇到死锁情况。当使用Invoke处理在后台线程上触发的事件时,甚至可能导致此问题。


真正的原因更多地与竞争条件有关,并在古代的Win32时代奠定了基础。我无法解释这里的细节,关键字是消息泵,WM_PAINT事件以及“SEND”和“POST”之间的细微差别。


可在此处找到更多信息herehere

答案 3 :(得分:2)

回到1.0 / 1.1在调试期间没有抛出任何异常,而是你得到的是一个间歇性的运行时挂起场景。太好了! :) 因此,使用2.0,他们使这个场景抛出一个异常,并且非常正确。

实际原因可能是(正如Adam Haile所述)某种并发/锁定问题。 请注意,正常的.NET API(例如TextBox.Text =“Hello”;)包装SEND命令(需要立即执行操作),如果在执行更新操作的线程上执行单独的线程,则会产生问题。使用Invoke / BeginInvoke使用POST来代替行动。

有关SEND和POST here的更多信息。

答案 4 :(得分:1)

这样你就没有两件事情试图同时更新控件。 (如果CPU在写/读中间切换到另一个线程,则可能发生这种情况) 在多个线程之间访问共享变量时,需要使用互斥锁(或其他一些同步)的原因相同。

编辑:

  

在其他语言中,例如C ++   自由尝试这样做(没有   异常被抛出   WinForms),但你最终会学习   艰难的方式!

啊,是的......我在C / C ++和C#之间切换,因此比我应该更加通用,对不起......他是对的,你可以做到这一点C / C ++,但它会回来咬你!

答案 5 :(得分:1)

还需要在对同时调用敏感的更新函数中实现同步。对UI元素执行此操作在应用程序和操作系统级别上都会很昂贵,而对于绝大多数代码来说都是完全冗余的。

某些API提供了一种更改系统当前线程所有权的方法,因此您可以临时(或永久)从其他线程更新系统,而无需借助线程间通信。

答案 6 :(得分:-1)

嗯我不太确定,但我认为当我们有一个进度控件,比如等待条,进度条,我们可以从另一个线程更新它们的值,一切都很好,没有任何故障。