MTA控制台应用程序从多个线程调用sta com对象



尽管有关com和sta/mta有很多疑问(例如,此处),但大多数人都在谈论具有UI的应用程序。但是,我有以下设置:

  • 默认情况下是一个控制台应用程序(Main(Main()明确具有[MTAThread]属性)。
  • 主线程产生了一些工作线程。
  • 主线程实例化单线读取com对象。
  • 主线程调用Console.Readline(),直到用户命中'q',然后应用程序终止。

一些问题:

  • 许多地方提到了COM对象的消息泵的需要。我需要为主线程手动创建一个消息泵,还是CLR在新的STA线程上为我创建它,因为这个问题建议?
  • 只是为了确保 - 假设CLR自动创建必要的管道,然后可以使用任何工作线程中的com对象而无需明确同步?
  • 在性能方面,以下哪项更好:
    • 让CLR照顾往返于COM对象的编组。
    • 明确在单独的sta线程上实例化对象,并通过例如ConcurrentQueue

这是通过com自动完成的。由于您的COM对象是单线读取的,因此Com 需要 适合对象以确保以线程安全的方式使用它。由于您的主线程不够友好,无法提供此类保证,因此COM会自动创建另一个线程并在该线程上创建对象。该线程也会自动泵送,您无需做任何帮助。您可以看到它是在调试器中创建的。启用非托管调试并查看"调试 Windows 线程"窗口。当您跨越 new 调用。

时,您会看到线程会添加。

很好,很容易,但是确实有一些后果。首先,COM组件需要提供代理/存根实现。知道如何序列化方法调用参数的辅助代码,以便可以在另一个线程上进行真实的方法调用。这通常是提供的,但并非总是如此。如果丢失了E_NOInterface异常,您将很难诊断。有时type_e_libnotreged,一个常见的安装问题。

最重要的是,每个 com组件都将被填写。这很慢,一个封闭的通话通常比直接呼叫速度慢于10,000倍,而直接呼叫本身就花费了很少的时间。就像属性Getter电话一样。当然,这确实可以陷入困境。

sta线程避免了这种情况,因此是使用单线读取组件的推荐方法。是的,这是STA线程泵送消息循环的要求。.NET程序中的Application.run()。这是元帅在com中从一个线程调用的消息循环。请注意,这并不一定意味着您必须必须具有消息循环。如果没有呼叫需要填写或换句话说,如果您从同一线程中对组件上的呼叫进行调用,则不需要消息循环。这通常很容易保证,尤其是在控制台模式应用程序中。如果您自己创建线程当然不是。

另外一个讨厌的细节:一个单线读取的com组件有时假设它是在泵送的线程上创建的。并将使用postmessage()本身,通常是在内部使用Worker线程并需要在STA线程上提出事件时。当您不抽水时,当然会不再正常工作。通常,您可以通过注意到事件没有提出来诊断这一点。这种组件的常见示例是WebBrowser。它在内部使用线程大量使用,但在创建其创建的线程上提出了事件。如果您不抽水,您将永远不会获得DocumentComplet的事件。

因此,即使没有打电话给application.run(),将[stathread]放在您的main()方法上可能足以获得快乐的快速代码。只需牢记后果,看到一个方法呼叫僵局或不增加事件的事件是需要抽水的迹象。

是的,可以从 mta thread。

在这种情况下, com (不是 clr )将创建一个隐式sta公寓(单独的com拥有的线程)或重新使用现有的公寓,创建的ealier。COM对象将在此实例化,然后将为其创建一个线程安全的代理对象(COM编组包装器),并返回到MTA线程。com将对MTA线程上制作的对象的所有呼叫都将与该隐式STA公寓进行编组。

这种情况通常是不受欢迎的。它有很多缺点,如果COM无法将对象的某些接口保存,则可能根本无法正常工作。检查此问题以获取更多详细信息。此外,邮件泵循环由隐式STA公寓运行,仅泵送有限数量的COM特定消息。这也可能影响com的功能。

您可以尝试一下,它可能对您有效。或者,您可能会遇到一些令人不安的问题,例如僵局,很难诊断。

这是我最近回答的一个密切相关的问题:

stataskscheduler和sta线程消息抽水

我个人更喜欢手动控制线间呼叫和线程亲和力的逻辑,并在我的答案中提出了类似ThreadAffinityTaskScheduler的逻辑。

您可能还需要阅读以下内容:信息:强烈建议使用OLE线程模型的描述和工作。

我需要手动为主线程创建消息泵吗

否。它在MTA中,因此不需要消息泵。

或CLR会在新的STA线程上为我创建它

如果com创建线程(因为过程中没有sta),那么它也会创建消息泵(以及隐藏的窗口:可以使用间谍 和类似的调试工具看到)。

com对象来自任何工作线程,而无需明确同步

取决于。

如果在MTA中创建了对单线螺纹对象(STO)的引用,则COM将提供适当的代理。此代理非常适合MTA中的所有线程。

在任何其他情况下,都需要编组参考以确保其具有正确的代理。

在性能方面更好

唯一的答案是测试和比较。

(请记住,如果您为sta创建线程,然后在本地实例化泵送的对象。我不清楚我有任何CLR级别轻量级消息泵泵 - 包括Winforms,只是为此肯定't。)

nb。COM和CLR的唯一深入解释性覆盖范围是 .net和com:Adam Nathan的完整互操作性指南(Sams,2002年1月)。但这是基于.NET 1.1的,现在已经绝版(但是有Kindle版本,可通过Safari Books在线获得)。即使这本书也无法直接描述您要做的事情。我建议一些原型。

最新更新