代码之家  ›  专栏  ›  技术社区  ›  Benjol

国家假人协会

  •  103
  • Benjol  · 技术社区  · 14 年前

    我刚刚用这个修正了一个错误:

    _Thread.SetApartmentState(ApartmentState.STA);
    

    现在我想了解它的含义,以及它为什么有效!

    1 回复  |  直到 14 年前
        1
  •  220
  •   Hans Passant    5 年前

    COM是.NET之父。他们有着相当高的目标,COM做的事情之一是.NET完全跳过了为类提供线程保证。一个COM类可以发布它有什么样的线程需求。COM基础设施确保满足这些要求。

    这在.NET中完全不存在。例如,您可以在多个线程中使用Queue<>对象,但如果您没有正确锁定,您的代码中将出现一个很难诊断的严重错误。

    COM线程的确切细节太大,无法放在文章中。我会把重点放在你问题的细节上。创建COM对象的线程必须告诉COM它希望为具有受限线程选项的COM类提供何种支持。这些类中的绝大多数只支持所谓的单元线程,它们的接口方法只能从创建实例的同一线程安全地调用。换句话说,他们宣布“我不支持线程,请注意” 从未 打错电话给我”。即使客户端代码实际上 从另一个线程调用它。

    有两种,STA(单线程公寓)和MTA。它是在CoInitializeEx()调用中指定的,任何使用COM执行任何操作的线程都必须调用该函数。CLR在启动线程时会自动进行调用。对于程序的主启动线程,它从main()方法的[STAThread]或[MTAThread]属性获取要传递的值。默认为MTA。对于您自己创建的线程,这取决于对SetApartmentState()的调用。默认为MTA。线程池线程始终是无法更改的MTA。

    Windows中有很多代码需要STA。值得注意的例子是剪贴板、拖放和shell对话框(比如OpenFileDialog)。还有很多你看不到的代码,比如UI自动化程序和观察消息的钩子。这些代码都不必是线程安全的,如果不知道它在哪个程序中使用,它的作者将很难保证代码的安全。因此,WPF或Windows窗体项目的UI线程必须始终是STA以支持此类代码,创建窗口的任何线程也必须是STA。

    但是你向COM承诺你的线程是STA 要求你遵守单线程公寓合同。他们是相当僵硬,你可以得到棘手的诊断故障时,你违反合同。所需的条件是,您永远不会在任何时间阻塞线程,并且您可以泵送消息循环。后一个需求由WPF或Winforms的UI线程满足,但是如果您创建自己的STA线程,则需要自己处理它。对违约的常见诊断是僵局。

    CLR中有很多内置的支持来支持这些需求btw,帮助您避免麻烦。这个 statement和WaitOne()方法在STA线程上阻塞时泵送消息循环。然而,这只处理了never block需求,您仍然需要创建自己的消息循环。WPF和Winforms中的Application.Run()。

    我之前提供了一个答案,其中包含了更多关于有一个消息循环保持COM快乐的重要性的细节。你会发现 post here .