Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# MTA控制台应用程序从多个线程调用STA COM对象_C#_Com Interop_Sta_Mta - Fatal编程技术网

C# MTA控制台应用程序从多个线程调用STA COM对象

C# MTA控制台应用程序从多个线程调用STA COM对象,c#,com-interop,sta,mta,C#,Com Interop,Sta,Mta,尽管有很多关于COM和STA/MTA的问题(例如),但大多数问题都是关于具有UI的应用程序。一、 但是,请执行以下设置: 控制台应用程序,默认情况下是多线程单元(Main()显式具有[MTAThread]属性) 主线程生成一些工作线程 主线程实例化一个单线程COM对象 主线程调用Console.ReadLine(),直到用户点击“q”,然后应用程序终止 有几个问题: 许多地方提到了这个问题。我是否需要为主线程手动创建消息泵,或者如问题所示,CLR是否会在新的STA线程上为我创建消息泵 只是

尽管有很多关于COM和STA/MTA的问题(例如),但大多数问题都是关于具有UI的应用程序。一、 但是,请执行以下设置:

  • 控制台应用程序,默认情况下是多线程单元(Main()显式具有
    [MTAThread]
    属性)
  • 主线程生成一些工作线程
  • 主线程实例化一个单线程COM对象
  • 主线程调用Console.ReadLine(),直到用户点击“q”,然后应用程序终止
有几个问题:

  • 许多地方提到了这个问题。我是否需要为主线程手动创建消息泵,或者如问题所示,CLR是否会在新的STA线程上为我创建消息泵
  • 只是为了确保——假设CLR自动创建了必要的管道,那么我可以从任何工作线程使用COM对象而不需要显式同步吗
  • 以下哪项性能更好:
    • 让CLR处理与COM对象之间的封送处理
    • 在单独的STA线程上显式实例化该对象,并让其他线程通过(例如)与之通信

是的,可以从MTA线程创建STA COM对象

在这种情况下,COM(不是CLR)将创建一个隐式的STA单元(一个单独的COM拥有的线程)或重用以前创建的现有单元。COM对象将在那里实例化,然后为其创建线程安全代理对象(COM封送包装器),并返回给MTA线程。在MTA线程上对对象的所有调用都将由COM封送到该隐式STA单元

这种情况通常是不可取的。它有很多缺点,如果COM无法封送对象的某些接口,可能根本无法按预期工作。查看更多详细信息。此外,隐式STA单元运行的消息泵循环仅泵送有限数量的特定于COM的消息。这也可能会影响COM的功能

你可以试试,它可能对你很有效。或者,您可能会遇到一些不愉快的问题,如死锁,很难诊断

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

我个人更喜欢手动控制线程间调用和线程关联的逻辑,在我的回答中提出了类似的建议

您可能还想阅读以下内容:,强烈推荐

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

不需要。它位于MTA中,因此不需要消息泵

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

如果COM创建线程(因为进程中没有STA),那么它也会创建消息泵(和一个隐藏窗口:可以通过SPY++和类似的调试工具看到)

COM对象,而无需显式同步

视情况而定

如果对单线程对象(STO)的引用是在MTA中创建的,则COM将提供相应的代理。此代理适用于MTA中的所有线程

在任何其他情况下,都需要封送引用以确保其具有正确的代理

在性能方面更好

唯一的答案是测试和比较两者

(请记住,如果您为STA创建线程,然后在本地实例化对象,则需要进行消息泵送。我不清楚是否存在任何CLR级别的轻量级消息泵,包括WinForms,仅此而已,当然不是。)


注意。COM和CLR的唯一深入解释是.NET和COM:Adam Nathan的完整互操作性指南(Sams,2002年1月)。但是它是基于.NET1.1的,现在已经绝版了(但是有Kindle版本,可以通过下载)。即使这本书也没有直接描述你想做什么。我会建议一些原型设计。

这是由COM自动完成的。因为您的COM对象是单线程的,所以COM需要为该对象提供一个合适的主页,以确保以线程安全的方式使用它。由于主线程不够友好,无法提供这样的保证,COM会自动创建另一个线程并在该线程上创建对象。这个线程也会自动抽水,你不用做任何帮助。您可以看到它正在调试器中创建。启用非托管调试并查看“调试+窗口+线程”窗口。当您跳过新的调用时,您将看到线程被添加

很好,很简单,但它确实有一些后果。首先,COM组件需要提供代理/存根实现。帮助程序代码,知道如何序列化方法调用的参数,以便在另一个线程上进行真正的方法调用。这通常是提供的,但并不总是。如果缺少E_NOINTERFACE异常,您将得到一个难以诊断的E_NOINTERFACE异常。有时键入_E_libnotregisted,这是一个常见的安装问题

最重要的是,COM组件上的每个调用都将被封送。这很慢,封送调用通常比直接调用花费很少时间的方法慢10000x左右。就像一个属性getter调用。这当然会让你的计划陷入困境

STA线程可以避免这种情况,因此建议使用单线程组件。是的,STA线程需要泵送消息循环。.NET程序中的Application.Run()。它是消息循环,用于封送COM中从一个线程到另一个线程的调用。请注意,这并不一定意味着您必须有一个消息循环。如果不需要封送任何调用,或者换句话说,如果从同一线程对组件进行所有调用,则不需要消息循环。这通常很容易保证,尤其是在一家公司