我注意到保存MailItem并释放是一项耗时的任务。那么,做以下事情是安全的吗? (下面的伪代码)
Thread 1 (main thread)
- Open 10 (different .msg files) - MailItems [List<MailItem> items]
- user works on them and want to save and close all of them with one click.
- On_save_All_click (runs on main thread)
- Do
- toBeClearedList.addAll(items);
- items.clear() [so that main thread cannot access those items]
- BG_Thread.ExecuteAsyn(toBeClearedList);
- End
Thread 2 (background thread) (input - List<MailItems>)
- foreach(MailItem item in input)
item.save();
System.Runtime.InteropServices.Marshal.ReleaseComObject(item)
- done
我写了几个测试,看起来像是在工作;只是想知道这样做是否安全? “在不同于创建它的线程中释放COM对象”
由于
Karephul
答案 0 :(得分:5)
从非托管代码(C / C ++)使用COM时,规则非常严格:您只能在获取对象的同一公寓中调用接口上的方法。因此,如果在STA线程上获得接口指针,则只允许该线程调用任何方法。如果在MTA线程上获得接口指针,则只有同一MTA中的其他线程才能使用该指针。跨越公寓的任何其他用途都要求将界面指针封送到另一个公寓。
但是,这是一个无人问津的世界。 .Net在COM上添加了一个完整的层,它掩盖了很多这些低级细节,而且大多数情况下,一旦你开始接触界面,你可以随心所欲地在线程之间传递该接口而不需要不得不担心旧的线程规则。这里发生的是它实际上传递了对称为“运行时可调用包装器”(RCW)的对象的引用,它正在管理底层的COM接口,并相应地控制对它的访问。 (它承担了维护COM公寓规则的负担,因此您不必这样做,这就是为什么旧的COM线程规则不适用于.Net:他们这样做,它们只是隐藏在你。)
所以你可以安全地从另一个线程调用Release或其他方法:但要注意,如果原始线程是STA,那么调用这些方法将导致底层RCW将调用编组回原始拥有线程,这样它仍然坚持基础COM规则。因此,使用单独的线程最终可能无法让您获得任何性能!
有些值得阅读的文章在这里填写了一些细节:
The mapping between interface pointers and runtime callable wrappers (RCWs) - 很好地概述了RCW如何工作的一些细节,但它没有说明线程。
Improving Interop Performance - Marshal.ReleaseComObject - 关于何时使用或不使用ReleaseComObject的一些好注释,以及它如何与RCW一起使用。
cbrumme's WebLog - Apartments and Pumping in the CLR - 比您可能想知道更多的内部结构;这是一些过时的CLR版本,但仍然可以很好地了解.Net如何涵盖许多基础COM问题。
答案 1 :(得分:2)
如果我记得我的COM正常(我总是讨厌那种技术,太复杂了),如果COM对象是单线程的(即属于单线程公寓),那么从另一个线程释放它就不是'对你有好处 - 它只是在主线程中执行实际的发布代码。
你 会看到你的代码之间的一点点差别,并在一个循环中从主线程中释放对象。如果在一个循环中释放对象,则在释放所有消息之前,UI将无响应。通过使用辅助线程,您的UI线程将释放一条消息,然后处理其他事件,然后释放另一条消息,依此类推。您可以通过向自己发送消息(或者如果您有WPF应用程序时使用Dispatcher)获得相同的效果,并避免使用其他线程。