为什么WPF中的UI控件具有线程亲和性?

12

为什么只有创建控件的线程才能更新它?为什么微软没有给人们在多线程读写UI控件属性时使用锁和其他线程同步技术的能力。


2
MSDN上的MS回答:“最终,WPF的线程模型与现有的User32线程模型保持同步,采用单线程执行和线程亲和力。这样做的主要原因是为了实现互操作性 - 诸如OLE 2.0、剪贴板和Internet Explorer等系统都需要单线程亲和力(STA)执行。” - Andy
2个回答

14

根据MSDN提供的简短描述:

WPF的线程模型与现有的User32线程模型保持同步,使用单线程执行和线程亲和力。 主要原因是为了实现互操作性 - 诸如OLE 2.0、剪贴板和Internet Explorer等系统都需要单线程亲和力(STA)执行。

更详细的描述如下:

大多数WPF对象都派生自DispatcherObject,它提供了处理并发和线程的基本结构。WPF基于由调度程序实现的消息系统。这与熟悉的Win32消息泵非常相似。事实上,WPF调度程序使用User32消息执行跨线程调用。
在讨论WPF中的并发性时,有两个核心概念需要理解——调度程序和线程亲和性。
在WPF的设计阶段,目标是转向单个执行线程,但采用非线程“亲和”模型。当组件使用执行线程的标识来存储某种类型的状态时,就会出现线程亲和性。这种情况最常见的形式是使用线程本地存储(TLS)来存储状态。线程亲和性要求每个逻辑执行线程只被操作系统中的一个物理线程拥有,这可能会占用大量内存。最终,WPF的线程模型与现有的User32线程模型保持同步,即单线程执行模型与线程亲和性。这样做的主要原因是互操作性——像OLE 2.0、剪贴板和Internet Explorer等系统都需要单线程亲和性(STA)执行。
鉴于您拥有具有STA线程的对象,您需要一种在线程之间通信的方法,并验证您是否在正确的线程上。这就是调度程序的作用。调度程序是一个基本的消息分发系统,具有多个优先级队列。消息的示例包括原始输入通知(鼠标移动)、框架函数(布局)或用户命令(执行此方法)。通过从DispatcherObject派生,您创建了一个具有STA行为的CLR对象,并在创建时给它一个指向调度程序的指针。

您可以在这里阅读完整文章。

个人而言,我更喜欢WPF的单线程模型,而不是使用锁定和线程同步技术。Dispatcher可用于以不同的优先级级别将消息传递到主UI线程,这处理了大部分小型后台进程,如果需要任何重度处理,则仍然可以为此创建自己的后台线程。


只是为了解释为什么更喜欢使用调度而不是锁/信号量等。调度程序本质上是一个带有单线程消费者的队列 - 即Actor。演员与许多可靠的文献相关联。此外,将生成/消耗代码与并发代码隔离可以帮助防止开发人员使用共享状态杀死缓存 - 这种效果通常会使细粒度并发的多线程代码明显慢于单线程代码。 - Seth

4

WPF,就像几乎所有的UI工具包一样,通过消息循环来工作。由于消息可以随时到来并影响任何控件,因此您需要全局锁定。为了使其更少出错,您可能需要一个在锁定下调用委托的函数。也许像这样:

Dispatcher.Invoke(Delegate, Object())

这个被调度到UI线程而不是获取全局锁的事实只是一个实现细节。

3
简而言之,WPF是建立在旧的遗留技术之上,为开发人员提供了一个华丽友好的界面。因此采用了STA模型。 - Andy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接