STA(单线程单元)COM 对象 - 生成工作线程?
在 STA COM 对象中生成工作线程(即 COM 对象创建一个线程来执行任务)是一件坏事吗?我想,答案是——这要看情况!
以我为例: 我正在使用的工作线程不会干扰/访问 COM 或 COM 服务。
我之所以问这个问题,是因为根据 STA COM 定义,STA 只能容纳一个线程。生成多个线程有点违反这一原则,除非工作线程及其工作不干扰/处理 COM/COM 服务。 在这种情况下,我认为这完全没问题,并且在我看来,COM 不应将工作线程视为逻辑 STA 的一部分。
您对此有何看法?
Is it a bad thing to spawn worker threads in your STA COM object (ie. COM object creates a thread to perform a task)? I think, the answer is - that depends!
For example in my case:
The worker threads that I am using will not interfere/access COM or COM Services.
Reason why I am asking this is because by STA COM definition STA can only house one thread. Spawning multiple threads kind of goes against this principle unless the worker threads and the work they do NOT interfere/deal with COM/COM services.
In this case I am thinking this is perfectly fine and in my opinion the worker threads should not be considered by COM as part of the logical STA.
What are your thoughts on this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不,这并不是一件坏事。公寓明确存在是为了帮助您使多线程代码正常工作。 STA 线程是非线程安全的 COM 服务器的安全之家,COM 的单元线程模型确保它始终以线程安全的方式使用。您所要做的就是编组要在工作线程中使用的接口指针(例如 IGlobalInterfaceTable),并且无需执行任何特殊操作即可调用这些方法。
当然,这不是免费的,整理调用会产生开销。多少取决于 STA 线程在泵送消息循环时的响应程度。如果您打算显式创建工作线程以以多线程方式使用 COM 服务器,那么您当然不会领先,反而会变慢。
No, that's not a bad thing. Apartments explicitly exist to help you getting multi-threaded code working. An STA thread is a safe home for a COM server that's not thread-safe, COM's apartment threading model ensures that it is always used in a thread-safe way. All you have to do is the marshal the interface pointer you want to use in the worker thread (IGlobalInterfaceTable for example) and you can call the methods without doing anything special.
This doesn't come for free of course, there's overhead involved in marshaling the call. How much depends on how responsive the STA thread is when it pumps its message loop. If you intended to create the worker thread explicitly to use that COM server in a multi-threaded way then of course you'll not be ahead, you made it slower.
不要让工作线程以任何方式使用 COM,这样应该没问题。这意味着您无法在工作线程中调用 COM 对象,也无法从工作线程中直接或间接调用 COM 运行时 API。
Don't let the worker threads use COM in any way, and you should be fine. This means you can't call COM objects in the worker and you can't call COM runtime APIs from the worker... either directly or indirectly.
需要认识到的重要一点是,您创建的任何新线程本身就是新线程;实际上哪个线程创建它们并不重要。重要的两件事是:(1) 这些新线程本身调用 CoInitializeEx 并各自获取自己的 STA,或者一起共享 MTA,以及 (2) 在线程之间传输的任何 COM 对象指针都得到适当的封送。切勿仅在全局变量中将 COM 对象指针从一个线程传递到另一个线程;而是使用 GIT 或 CoMarshalInterThreadInterfaceInStream(视情况而定)。
(对此有一个例外:您可以在 MTA 线程之间自由传递 COM 指针;但前提是该指针首先已被适当地编组到 MTA 中。)
此外,您需要非常了解存在的对象以及它们的关联性是。如果您在 STA 线程上创建一个对象,并将指针编组到另一个线程,则典型情况是该对象仍将驻留在原始 STA 线程上,并且调用返回到该线程,除非您采取特定步骤来指定。 (这里要注意的事情是:对象的线程模型是什么,以及它是否“聚合自由线程编组器”。)
所以这不是一件坏事;它是一个很好的选择。但请确保您的做法正确。例如,您可能认为使用两个线程可能会更有效;但后来意识到,工作线程花费了大量时间回调原始线程上的对象,这给你带来了比单线程情况更差的性能。因此,您需要首先仔细考虑您的线程和对象策略。
(话虽如此,您当然可以根据需要启动任意数量的不调用 CoInitialize 的线程,只要它们不以任何方式使用 COM 或 COM 对象;如果这些线程需要以某种方式进行通信对于使用 COM 的线程,您可以使用您选择的任何“经典”IPC 机制(例如消息、全局变量等)来管理该通信。
The important thing to realize is that any new threads you create are new threads in their own right; it actually doesn't matter at all which thread created them. The two things that matter are: (1) that those new threads themselves call CoInitializeEx and either get their own STA each, or share an MTA together, and (2) any COM object pointers you transfer between threads get marshaled appropriately. Do not ever just pass a COM object pointer from one thread to another in a global variable; instead use the GIT or CoMarshalInterThreadInterfaceInStream as appropriate.
(One exception to this: you can pass COM pointers freely between MTA threads; but only once that pointer has been appropriately marshaled into the MTA in the first place.)
Also, you need to be very aware of there objects live and what their affinities are. If you create an object on a STA thread, and marshal a pointer to another thread, then the typical case is that the object will still live on that original STA thread with calls returning to that thread, unless you takes specific steps to specify otherwise. (Things to watch for here: what the object's threading model is, and whether it 'aggregates the free-threaded marshaller'.)
So it's not a bad thing; but be sure that you do it appropriately. For example, you might think that using two threads might be more efficient; but then later on realize that a lot of time is being spent by that worker thread calling back to the object on the original thread, giving you worse performance than a single-threaded case. So you need to think out your threads and object strategy carefully first.
(Having said all of that, you can of course spin up as many threads as you want that don't call CoInitialize, so long as they don't use COM or COM objects in any way; if those threads to need so somehow communicate with the threads that do use COM, it's up to you to manage that communication using any 'classic' IPC mechanism of your choice - eg. messages, globals, etc.)