我知道如果我从不同的线程修改控件,我应该小心,因为WinForms和WPF不允许从其他线程修改控件的状态。
为什么会出现这种限制?
如果我可以编写线程安全的代码,我应该能够安全地修改控制状态。那么为什么会出现这种限制呢?
答案 0 :(得分:23)
有几个GUI框架具有此限制。根据书Java Concurrency in Practice,其原因是为了避免复杂的锁定。问题是GUI控件可能必须对来自UI,数据绑定等的两个事件作出反应,这导致从几个不同的源锁定并因此导致死锁的风险。为了避免这种情况,.NET WinForms(和其他UI)将对组件的访问限制为单个线程,从而避免锁定。
答案 1 :(得分:9)
在Windows的情况下,当创建控件时,通过来自消息泵的消息执行UI更新。程序员没有直接控制泵运行的螺纹,因此控制消息的到达可能导致控制状态的改变。如果允许另一个线程(程序员直接控制)改变控件的状态,则必须设置某种同步逻辑以防止控制状态的损坏。 .Net中的控件不是线程安全的;这是我怀疑的设计。在设计,开发,测试和支持提供此功能的代码方面,将同步逻辑放在所有控件中将是昂贵的。程序员当然可以为控件提供线程安全性,用于他自己的代码,但不能用于与他的代码同时运行的.Net中的代码。这个问题的一个解决方案是将这些类型的操作限制为仅一个线程和一个线程,这使得.Net中的控制代码更容易维护。
答案 2 :(得分:3)
.NET保留随时在您创建控件的线程中访问控件的权利。因此,来自另一个线程的访问永远不会是线程安全的。
答案 3 :(得分:2)
您可能能够使自己的代码成为线程安全的,但是您无法将必要的同步原语注入到与代码中的代码匹配的内置WinForm和WPF代码中。请记住,有许多消息在幕后传递,最终导致UI线程访问控件而你却没有意识到它。
控件线程关联性的另一个有趣的方面是可以(尽管我怀疑他们永远不会)使用Thread Local Storage模式。显然,如果你在一个线程上访问一个控件而不是在它上面创建一个控件,那么无论你如何仔细地构造代码来防止多线程代码的所有正常问题,它都无法访问正确的TLS数据。
答案 4 :(得分:1)
实际上,据我所知,从一开始就是这个计划!每个控件都可以从任何线程访问!并且仅仅因为当另一个线程需要访问控件时需要线程锁定 - 并且因为锁定很昂贵 - 所以制作了一个新的线程模型,称为“线程租用”。在该模型中,相关控件将仅使用一个线程聚合到“上下文”中,从而减少所需的锁定量。 很酷,嗯?
不幸的是,这种尝试太大胆而不能成功(而且更复杂,因为仍然需要锁定),所以好的旧Windows窗体线程模型 - 使用单个UI线程和创建线程来声明所有权控制 - 在wPF再次使用,让我们的生活更轻松?
答案 5 :(得分:1)
Windows支持许多操作,特别是组合使用,本质上不是线程安全的。应该怎么办,例如,如果一个线程试图将一些文本插入到以第50个字符开头的文本字段中,而另一个线程试图从该字段中删除前40个字符? Windows可能会使用锁来确保第二个操作在第一个操作完成之前无法启动,但是使用锁会增加每个操作的开销,并且如果一个实体上的操作需要,也会增加死锁的可能性操纵另一个人。要求涉及特定窗口的操作必须在特定线程上发生,这比防止不安全的操作组合同时执行所需的要求更严格,但是相对容易分析。使用来自多个线程的控件并通过其他方式避免冲突通常会更加困难。