C# 在后台线程中释放COM对象

C# 在后台线程中释放COM对象,c#,com,outlook,C#,Com,Outlook,延伸至 我注意到保存邮件并发布邮件是一项耗时的任务。那么,这样做安全吗?(下面是伪代码) 线程1(主线程) -打开10(不同的.msg文件)-邮件项目[列表项目] -用户对它们进行操作,并希望通过单击保存和关闭所有它们。 -单击保存所有(在主线程上运行) -做 -toBeClearedList.addAll(项目); -items.clear()[以便主线程无法访问这些项] -BG_Thread.ExecuteAsyn(toBeClearedList); -结束 线程2(背景线程)(输入-列表)

延伸至

我注意到保存邮件并发布邮件是一项耗时的任务。那么,这样做安全吗?(下面是伪代码)

线程1(主线程)
-打开10(不同的.msg文件)-邮件项目[列表项目]
-用户对它们进行操作,并希望通过单击保存和关闭所有它们。
-单击保存所有(在主线程上运行)
-做
-toBeClearedList.addAll(项目);
-items.clear()[以便主线程无法访问这些项]
-BG_Thread.ExecuteAsyn(toBeClearedList);
-结束
线程2(背景线程)(输入-列表)
-foreach(输入中的MailItem)
item.save();
System.Runtime.InteropServices.Marshal.ReleaseComObject(项)
-完成
我写了两个测试,看起来很有效;只是想知道这样做是否安全?“在与创建COM对象所在线程不同的线程中释放COM对象”

谢谢


Karephul

如果我正确地记得我的COM(我一直讨厌这种技术,太复杂了),如果COM对象是单线程的(也就是说,属于单线程单元),从另一个线程释放它不会对你有任何好处-它只会在你的主线程中执行实际的发布代码


您将看到代码与在一个循环中从主线程释放对象之间的一些区别。如果在一个循环中释放对象,在释放所有消息之前,UI将没有响应。通过使用辅助线程,UI线程将发布一条消息,然后处理其他事件,然后发布另一条消息,依此类推。您可以通过向自己发送消息(如果您有WPF应用程序,也可以使用Dispatcher)来获得相同的效果,并避免使用另一个线程。

当从非托管代码(C/C++)使用COM时,规则非常严格:您只能在获取对象的同一单元的接口上调用方法。因此,如果您在STA线程上获得接口指针,则只允许该线程调用任何方法。如果在MTA线程上获取接口指针,则只有同一MTA中的其他线程可以使用该指针。跨单元的任何其他用途都需要将接口指针封送到其他单元

然而,这是一个不变的世界。Net在COM之上添加了一个完整的层,其中隐藏了大量这些低级细节,并且在大多数情况下,一旦您获得了一个接口,您就可以在线程之间随意传递该接口,而不必担心旧的线程规则。这里发生的事情是,它实际上正在传递对一个称为“运行时可调用包装器”(RCW)的对象的引用,并且它正在管理底层COM接口,并相应地控制对它的访问。(它承担了维护COM单元规则的责任,这样您就不必这么做,这就是为什么旧的COM线程规则似乎不适用于.Net:它们适用,只是对您隐藏了。)

因此,您可以从另一个线程安全地调用Release或其他方法:但是请注意,如果原始线程是STA,那么调用这些方法将导致底层RCW将调用封送回原始拥有线程,以便它仍然支持底层COM规则。因此,使用一个单独的线程最终可能无法获得任何性能

一些值得一读的文章在这里补充了一些细节:

  • -很好地概述了RCW工作原理的一些细节,但没有说明太多关于线程的内容

  • -关于何时使用或不使用ReleaseComObject以及它如何与RCWs一起工作的一些好的说明

  • -比您可能想知道的更多的内部信息;这是几个过时的CLR版本,但仍然可以很好地洞察.Net如何覆盖许多基础COM问题


正如您所说,我唯一想要实现的就是“响应UI”。只要安全地启动另一个线程并安全地释放那些COM对象(没有任何不必要的异常),我就很好。(我会继续测试)
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