我确信这有一个很好的(或者至少是体面的)理由。它是什么?

有帮助吗?

解决方案

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

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

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

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

myControl.BeginInvoke(myControl.UpdateFunction);

这相当于从工作线程执行 C++/MFC PostMessage

其他提示

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

当然,唯一的原因是,框架中某个地方的线程不是很安全。

这个“东西”几乎是 System.Windows.Forms 中每个控件上的每个实例成员。

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

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

使每个实例成员线程安全可能会带来大多数应用程序不需要的大量开销。相反,.Net 框架的设计者决定(我认为是正确的)从多个线程同步访问表单控件的负担应该由程序员承担。

[编辑]

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

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

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

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


真正的原因更多地与竞争条件有关,并且可以追溯到古老的 Win32 时代。这里我无法解释细节,关键字是消息泵、WM_PAINT 事件以及“SEND”和“POST”之间的细微差别。


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

回到 1.0/1.1 时,调试期间不会引发异常,而是出现间歇性运行时挂起情况。好的!:)因此,有了2.0,他们使这种情况异常,这是正确的。

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

有关发送和发布的更多信息 这里.

这样您就不会同时尝试更新控件的两件事。(如果CPU在写入/读取的中间的CPU切换到另一个线程)时,当访问多个线程之间的共享变量时,您需要使用MUTEXES(或其他某些同步)的原因。

编辑:

在其他语言(例如C ++)中,您可以自由尝试执行此操作(在Winforms中不像Winform一样抛出),但是您最终会学习艰难的方式!

啊,是的...我在 C/C++ 和 C# 之间切换,因此比我应该的更通用一点,抱歉...他是对的,你 在 C/C++ 中执行此操作,但它会反过来咬你!

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top