我知道每个线程在与COM系统交互之前调用CoInitialize
的COM的要求。
.NET公开了一些内部在线程上运行的项目,例如:
ThreadPool
个帖子BackgroundWorker
类(使用异步委托(使用线程池线程))如果我要从一个线程与一个COM对象进行交互,我是否需要先调用CoInitialize
?
我问,因为可能有一些more magic that automagically calls it for me - 我不知道。
Managed and Unmanaged Threading
对于互操作性,公共语言运行库创建和 在调用COM对象时初始化公寓。托管线程 可以创建并输入包含的单线程单元(STA) 只有一个线程,或包含一个的多线程单元(MTA) 或更多线程。当COM公寓和线程生成的公寓 兼容,COM允许调用线程直接进行调用 到COM对象。如果公寓不兼容,COM会创建一个 兼容的公寓和编组所有通过代理在新的呼叫 公寓。
运行时调用CoInitializeEx将COM公寓初始化为 无论是MTA还是STA公寓。
更新二:
看起来你不应该使用.NET提供的任何类型的线程中的COM:
The Managed Thread Pool
有几种情况适合创建和 管理自己的线程而不是使用线程池线程:
您需要一个前台线程。
您需要一个具有特定优先级的线程。
您的任务导致线程长时间阻塞 时间。线程池有最大线程数,所以很大 被阻止的线程池线程数可能会阻止任务 开始。
您需要将线程放入单线程单元中。所有 ThreadPool线程位于多线程单元中。
您需要具有与该线程关联的稳定标识,或者 将线程专用于任务。
更新三:
看起来你 可以 设置无人管线程的线程模型:
Managed and Unmanaged Threading in Microsoft Windows
可以标记托管线程以指示它将托管单线程或多线程单元。 GetApartmentState类的SetApartmentState,TrySetApartmentState和Thread方法返回并指定线程的单元状态。如果尚未设置状态,则GetApartmentState会返回ApartmentState.Unknown。
只有当线程处于ThreadState.Unstarted状态时,才能设置该属性;它只能为一个线程设置一次。
如果在线程启动之前未设置单元状态,则将线程初始化为多线程单元(MTA)。
很多相互矛盾的信息。
这就是为什么我们会使用Stackoverflow上的任何人所说的真实答案。
答案 0 :(得分:10)
这里的信息实际上并没有冲突 - 如果你是COM的新手,那就不一定非常清楚了。
简短回答:
[STAThread]
属性添加到Main()
以请求运行时将主线程初始化为STA,或者在您之前创建的新线程上使用thread.SetApartmentState(ApartmentState.STA)调用thread.Start()
- 否则默认情况下它们是MTA。无论如何,一旦线程启动并运行,就无法修改线程单元模型。更长的答案:有两种方法可以调用CoInitialize - 您可以使用它来将线程初始化为单线程单元线程(STA),或者作为多线程单元线程(MTA)。上面的文字说的是默认情况下,新线程和线程池线程自动被预先CoInitialized为MTA-flavor。但是使用新线程,如果在实际启动线程之前执行此操作,则可以使用ApartmentState来指定STA风格。无论如何,它始终以这种或那种方式进行初始化。
请注意,基于UI的程序上的Main()标有[STAThread]属性,以确保它是基于STA的;在控制台应用程序上,缺少[STAThread]意味着它被作为MTA CoInited。顺便说一下,这个属性的原因是调用Main()的线程是你不能使用ApartmentState指定STA vs MTA的一个线程 - 因为它已经运行并且在Main()执行时,所以太晚了用那个;所以将该属性视为运行时提示在调用Main()之前设置单元状态。
需要注意的关键是STA通常与UI一起使用,需要一个消息循环(.Net WinForms为您提供); STA代码永远不应该使用Sleep()或类似代码阻止,否则您的UI也会阻塞。另一方面,MTA是为工作人员使用而设计的 - 例如,后台任务,下载文件或在后台进行计算,通常不应该拥有UI。您可以使用其中任何一个中的COM,但这可能取决于COM对象正在执行的操作或从何处获取它。如果它是一个UI组件,可能你想从STA线程中使用它;另一方面,如果它是用于下载或进行计算的组件,则通常在MTA线程中使用它。
上面的更新1基本上是说.Net运行时总是为你调用CoInitialize - 但是让你选择STA vs MTA,MTA是默认值。
上面的更新2基本上是说因为ThreadPool线程是MTA(并且你不能改变它),所以你应该只使用它们来进行后台操作,而不是将它们用于UI任务。
更新3表示,对于新线程,您可以选择MTA与STA - 与更新1相同,只是更明确地了解API。
整个MTA与STA之间的关系可能非常复杂,建议将this article作为起点。不过,大局主要通过记住STA =单线程和UI来总结; MTA =多个线程,后台/工作人员任务。 (STA vs MTA也适用于对象,而不仅仅是线程,COM在幕后做了大量工作,让不同类型的线程使用不同类型的对象。当它运行良好时,你没有意识到它和可以幸福地忽略它;但是当你遇到限制或限制时,弄清楚发生了什么通常很棘手。)
答案 1 :(得分:1)
要回答你的第一个问题,如果我正确地记住了我的Don Box,每个线程都必须调用CoInitialize。没有例外。
关于自动部分,我不知道。