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

时间:2020-03-05 18:39:29  来源:igfitidea点击:

我敢肯定有一个很好的理由(或者至少是体面的理由)。它是什么?

解决方案

回答

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

编辑:

In other languages such as C++ you are
  free to try and do this (without an
  exception being thrown as in
  WinForms), but you'll end up learning
  the hard way!

嗯,是的。。。我在C / C ++和Cand之间切换,因此本来应该更通用一些,对不起...他是正确的,我们可以在C / C ++中执行此操作,但是它会再次引起注意你!

回答

因为我们很容易陷入僵局(还有其他问题)。

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

在其他语言(例如C ++)中,我们可以随意尝试执行此操作(不会像WinForms中那样引发异常),但是如果发生死锁,应用程序可能会冻结并停止响应。

顺便说一句,我们可以轻松地告诉UI线程我们要更新控件,只需创建一个委托,然后在该控件上调用(异步)BeginInvoke方法即可将其传递给我们。例如。

myControl.BeginInvoke(myControl.UpdateFunction);

这等效于从工作线程执行C ++ / MFC PostMessage

回答

回到1.0 / 1.1,在调试过程中没有引发异常,而是出现了间歇性的运行时挂起情况。好的! :)

因此,在2.0版本中,他们使这种情况引发了异常,这是正确的。

造成这种情况的实际原因可能是(如亚当·海尔所言)某种并发/锁定问题。
请注意,普通的.NET api(例如TextBox.Text =" Hello";)包装了SEND命令(需要立即采取措施),这些命令如果在与执行更新操作不同的线程上执行,则可能会产生问题。使用Invoke / BeginInvoke使用POST代替,该POST将操作排队。

回答

有关发送和发布的更多信息,请点击此处。

还需要在对同时调用敏感的更新功能内实现同步。对UI元素执行此操作在应用程序和OS级别上都将是昂贵的,并且对于绝大多数代码而言是完全多余的。

回答

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

我认为这是一个绝妙的问题,我认为需要更好的答案。

当然,唯一的原因是框架中的某些内容不是非常线程安全的。 .NET或者Win32是否有问题?为什么不推动修复源代码而不是强制执行对我来说感觉像是一种讨厌的解决方法?

回答

I think this is a brilliant question -
  and I think there is need of a better
  answer.
  
  Surely the only reason is that there
  is something in a framework somewhere
  that isn't very thread-safe.

有人知道真正的潜在问题在哪里吗?

该"内容"几乎是System.Windows.Forms中每个控件上的每个实例实例成员。

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

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

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

[编辑]

尽管此问题仅问"为什么",但这里有指向解释"如何"的文章的链接:

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

回答

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

尽管听起来合理,但约翰斯的回答并不正确。实际上,即使使用Invoke,我们也无法避免陷入僵局。当使用Invoke处理在后台线程上触发的事件时,甚至可能导致此问题。

真正的原因与比赛条件有关,并且可以追溯到古代的Win32时代。我在这里无法解释细节,关键字是消息泵,WM_PAINT事件以及" SEND"和" POST"之间的细微差别。

回答

可以在此处和此处找到更多信息。

段落数量不匹配