我注意到保存和释放 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 对象”
谢谢
卡勒普尔
最佳答案
从非托管代码 (C/C++) 使用 COM 时,规则非常严格:您只能从获取对象的同一单元调用接口(interface)上的方法。因此,如果您获得 STA 线程上的接口(interface)指针,则只允许该线程调用任何方法。如果您获得 MTA 线程上的接口(interface)指针,则只有同一 MTA 中的其他线程可以使用该指针。任何其他跨单元的使用都需要将接口(interface)指针编码到其他单元。
然而,那是未受破坏的世界。 .Net 在 COM 之上添加了一个完整的层,其中隐藏了许多这些低级细节,并且在大多数情况下,一旦您掌握了接口(interface),就可以在线程之间尽可能多地传递该接口(interface),而无需不必担心旧的线程规则。这里发生的事情是,它实际上传递对一个称为“运行时可调用包装器”(RCW) 的对象的引用,并且它正在管理底层 COM 接口(interface),并相应地控制对其的访问。 (它承担了维护 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 问题。
关于c# - 在后台线程中释放 COM 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9282455/