【问题标题】:Thread Apartment State线程公寓状态
【发布时间】:2016-07-05 06:50:11
【问题描述】:

全部,

我在 C# 中使用线程已经有一段时间了,但我仍然对线程的单元状态的真正含义有些困惑。我知道 WinForms 必须始终使用 STA 公寓状态(而不是 MTA),但我仍然不清楚公寓状态的全部含义。

【问题讨论】:

标签: c# multithreading


【解决方案1】:

COM 有非常崇高的目标。其中之一是线程是一个很难正确处理的编程细节,应该由支持库管理。与 .NET 完全不同的是,以线程安全的方式使用非线程安全的类完全取决于您。

这通过注册表工作,COM coclass 发布 ThreadingModel 注册表项,说明它支持哪种线程。到目前为止,他们中的大多数都使用“Apartment”,这是一种不太清楚的方式来表示“我不支持多线程”。这是 COM 确保以线程安全方式调用所有方法的信号。如果您的程序从工作线程调用方法,那么 COM 负责将来自工作线程的调用封送到创建实例的线程。从而自动确保服务器以线程安全的方式使用。与 Control.Invoke 和 Dispatcher.Invoke 的工作方式没有什么不同,但完全是自动的。

这当然是美妙的魔法,但你的程序确实需要配合一下。没有您的帮助,COM 无法编组这样的呼叫。当你的程序创建一个线程时,它必须调用 CoInitialize() 来告诉 COM 基础结构你想参与 COM 调用。那时,您必须告诉它您创建的线程是什么种类。有两种,以“公寓”类型区分。有 STA(单线程单元)和 MTA(多线程)。 STA 线程是不支持线程的 COM 组件的好客之家。不是 MTA 线程。

不过,这种公寓附有价格标签。创建 STA 时,您必须遵循 STA 规则。哪个有点龙:

  • 必须启动 Windows 消息循环
  • 你可以永远阻塞线程

消息循环是 COM 编组从一个线程到另一个线程的调用的机制。需要永不阻塞规则来防止死锁。虽然很龙,但它是程序的 UI 线程的工作方式。这不是巧合。

在 MTA 线程上创建单元线程 COM 对象还附加了一个价格标签。这样的线程不是好家,它不遵循 STA 规则。 COM 无济于事,无法对方法调用进行封送处理。 COM 介入并实际创建自己的 STA 线程,为对象提供一个好客的家。不错,但便宜,因为这会消耗一个线程并且每个方法调用都被编组,这给每个调用增加了很多开销。

COM 线程支持非常好,它可以在 98% 的时间内处理所有事情,而无需做任何特别的事情。然而,这 2% 可能会给你带来严重的偏头痛,你几乎无能为力。它的扩展性也很差,这可能是 .NET 没有类似功能的最大原因。

虽然 COM 可能看起来已死,但公寓在 Windows 编程中仍然很重要。 .NET 框架对它们有明确的支持。例如,在 STA 线程上调用 Thread.Join() 或 Monitor.Enter(),由 COM 明确禁止,使 CLR 泵成为消息循环。其他工件是您在 GUI 应用程序的 Main() 方法和 Thread.SetApartmentState() 上看到的 [STAThread] 属性,它们是正确获取 CLR 调用 CoInitialize() 的方法。剪贴板、拖放和外壳对话框(如 OpenFileDialog)等 GUI 功能明确需要 STA 才能工作。创建任何窗口的线程应始终是 STA 线程。

【讨论】:

    【解决方案2】:

    Raymond Chen 的这篇关于 The Old New Thing 的博文介绍了 STA 和 MTA 线程模型背后的一些历史:

    User interface code + multi-threaded apartment = death

    [ . . . ] COM 长大了两种性格,一种 专注于 GUI 客户和 另一个专注于非 GUI 顾客。对于非 GUI 客户, 附加功能,例如 添加了多线程公寓, 而且由于客户没有做GUI 东西,多线程公寓 不受 GUI 规则的负担。 他们没有发消息给 相互交流;他们用 内核对象和 等待单个对象。每个人都赢了, 对吧?

    嗯,是的,每个人都赢了,但你 必须知道你的面包是哪一面 涂上黄油。如果你初始化一个 GUI 线程作为多线程单元, 你违反了假设 在哪个多线程公寓下 被发明了! [ . . . ]

    【讨论】:

    • 还是……这两种型号有什么区别?也许举个例子?
    • @koumides - 除了更改入口点的属性([STAThread] vs [MTAThread])外,C# 代码没有太大区别。 UI 线程必须是 STA。如果你弄错了,你可能会观察到错误的行为、崩溃、死锁等。我真的不知道你在寻找什么样的例子。如果您不使用 WinForms(使用 C、C++ 或带有 P/Invoke 的 C#)直接向 Win32 编码,那么,是的,会有差异。
    • @koumides - 简而言之,使用 C# 进行开发,我不太了解细节,也不必了解。请参阅 stackoverflow.com/q/127188/250918 以获得更好的问题答案。
    猜你喜欢
    • 2011-05-08
    • 1970-01-01
    • 2013-04-10
    • 2019-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-02
    相关资源
    最近更新 更多