在WinForms中,为什么可以';你不能从其他线程更新UI控件吗?

在WinForms中,为什么可以';你不能从其他线程更新UI控件吗?,winforms,multithreading,Winforms,Multithreading,我相信这有一个很好的理由(或者至少是正当的理由)。这是什么?这样您就不会有两件事同时更新控件。(如果CPU在写/读中间切换到另一个线程,就会发生这种情况) 这与在多个线程之间访问共享变量时需要使用互斥锁(或其他一些同步)的原因相同 编辑: 在其他语言中,如C++ 自由尝试并执行此操作(无需 在中引发的异常 WinForms),但最终您将学习 艰苦的道路 啊,是的……我在C/C++和C#之间切换,因此比我应该的更通用一些,对不起。。。他是对的,你可以用C/C++来做这件事,但它会回来咬你的 因为你

我相信这有一个很好的理由(或者至少是正当的理由)。这是什么?

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

编辑:

在其他语言中,如C++ 自由尝试并执行此操作(无需 在中引发的异常 WinForms),但最终您将学习 艰苦的道路


啊,是的……我在C/C++和C#之间切换,因此比我应该的更通用一些,对不起。。。他是对的,你可以用C/C++来做这件事,但它会回来咬你的

因为你很容易就会陷入僵局(以及其他问题)

对于exmaple,您的次线程可能正在尝试更新UI控件,但UI控件将等待次线程锁定的资源释放,因此两个线程最终都会等待对方完成。正如其他人所评论的,这种情况不是UI代码所独有的,而是特别常见的

在C++等其他语言中,您可以尝试这样做(没有异常如WiFras一样抛出),但是如果出现死锁,您的应用程序可能会冻结并停止响应。p> 顺便说一句,您可以很容易地告诉UI线程您想要更新一个控件,只需创建一个委托,然后调用该控件上的(异步)BeginInvoke方法将其传递给您的委托。例如

myControl.BeginInvoke(myControl.UpdateFunction);

这相当于在1.0/1.1中从工作线程执行C++/MFC PostMessage,在调试过程中没有引发异常,而是出现了间歇性运行时挂起场景。很好!:) 因此,在2.0中,他们让这个场景抛出一个异常,这是非常正确的

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


有关发送和发送的更多信息。

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

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

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

当然,唯一的原因是 框架中的某个地方有什么东西吗 这不是非常线程安全的

“某物”几乎是System.Windows.Forms中每个控件上的每个实例成员

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

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

使这些实例成员中的每一个都是线程安全的,这可能会带来许多大多数应用程序不需要的开销。相反,.Net framework的设计者决定(我认为这是正确的)将从多个线程同步访问表单控件的负担放在程序员身上

[编辑]

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

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


尽管听起来很合理,约翰的回答是不正确的。事实上,即使在使用Invoke时,如果没有遇到死锁的情况,您仍然不安全。在处理使用Invoke在后台线程上触发的事件时,甚至可能会导致此问题


真正的原因更多地与种族条件有关,可以追溯到远古的Win32时代。我无法在这里解释细节,关键词是消息泵、WM_绘画事件以及“发送”和“发布”之间的细微差别



更多信息可以在这里找到。

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

完全可能是可重入的,但不是线程安全的。考虑(f)= > {x+=1;f(x);x=1;}如果f(x)在一个线程上重返lambda但在多线程中在x上进行竞争,则很好地溢出。我不知道为什么这个答案被接受。这根本不是正确的答案。只要有多个线程,就会发生死锁。GUI程序中没有任何固有的东西使它们更有可能发生。此外,即使使用BeginInvoke(),它们也很容易发生。Brian Ensink的答案是正确的。-1:等待锁定的资源不是问题。问题在于竞态条件,它大量存在于任何不是设计用于多线程环境的代码中。您甚至可以通过设置为false来检查这一点,并观察无死锁操作,直到发生有趣的事情。