简体   繁体   English

当创建它的线程终止时,CLR是否释放了COM对象?

[英]Does a COM object get released by the CLR when the thread that created it terminates?

I have been unable to figure out how to search for confirmation on this suspicion, but I am seeing evidence that a COM object created on one thread is no longer available to other threads (COM object that has been separated from its underlying RCW cannot be used) once code stops executing on the thread that created it (and that thread may have terminated). 我一直无法弄清楚如何搜索这种怀疑的确认,但我看到证据表明在一个线程上创建的COM对象不再可用于其他线程(已经与其底层RCW分离的COM对象无法使用)一旦代码在创建它的线程上停止执行(并且该线程可能已终止)。 This is a really insidious problem to track down because I have calls to System.Runtime.InteropServices.Marshal.ReleaseComObject throughout my code, but I could not identify any of them were being called causing this error. 这是一个非常隐蔽的问题,因为我在整个代码中都调用了System.Runtime.InteropServices.Marshal.ReleaseComObject ,但是我无法识别它们中的任何一个被调用导致此错误。 Finally I came to the conclusion that the COM object was apparently being implicitly freed when the secondary thread stopped executing. 最后我得出结论,当辅助线程停止执行时,COM对象显然被隐式释放。 Could this be true? 这可能是真的吗? Is this documented behavior? 这是记录在案的行为?

Yes, a COM object tends to have strong thread affinity. 是的,COM对象往往具有强大的线程亲和力。 Threading is not a minor implementation detail in COM. 线程不是COM中的次要实现细节。 Unlike .NET, COM provides thread-safety guarantees for a COM class. 与.NET不同,COM为COM类提供了线程安全保证。 COM can publish the kind of threading it supports, with "apartment" (ie "not thread-safe") a very common choice. COM可以发布它支持的线程类型,“公寓”(即“非线程安全”)是一个非常常见的选择。 COM ensures that these requirements are met without the program having to do anything to help. COM确保满足这些要求,而无需程序必须提供任何帮助。 Marshaling a call from one thread to another so that the object is always used in a thread-safe way is automatic. 从一个线程到另一个线程的调用,以便始终以线程安全的方式使用该对象是自动的。 In .NET code you normally have to do this yourself, using Control.BeginInvoke or Dispatcher.BeginInvoke, for example. 在.NET代码中,您通常必须自己使用Control.BeginInvoke或Dispatcher.BeginInvoke这样做。

An automatic consequence of this is that a thread that owns one or more COM objects that's allowed to exit will automatically get these objects released. 这样做的一个自动结果是拥有一个或多个允许退出的COM对象的线程将自动释放这些对象。 This is necessary since there is no longer a way to meet the thread safety requirement. 这是必要的,因为不再有办法满足线程安全要求。 Trying to use them anyway after this is going to bomb. 在这之后试图使用它们会炸弹。 There is no cure for this beyond ensuring that the thread stays alive long enough to keep servicing these objects. 除了确保线程保持足够长的时间以保持对这些对象的维护之外,没有办法解决这个问题。 Similarly, you'd need to keep the UI thread alive long enough to ensure that Dispatcher.BeginInvoke can still work in .NET. 类似地,您需要保持UI线程的活动时间足够长,以确保Dispatcher.BeginInvoke仍然可以在.NET中工作。

Fwiw, yes, using Marshal.ReleaseComObject() can give you plenty of fud about this. Fwiw,是的,使用Marshal.ReleaseComObject()可以给你很多关于这一点。 Explicit memory management has a long history of producing buggy programs, and automatic garbage collection provided the cure. 显式内存管理具有生成错误程序的悠久历史,并且自动垃圾收集提供了治愈。 GC is quite capable of getting that COM object released without your help and never gets it wrong. GC非常有能力在没有你帮助的情况下发布COM对象,并且永远不会出错。 It just takes a bit longer to get around to it. 它只需要更长的时间来解决它。 If you know that the COM object has unusually high resource usage that warrants releasing it deterministically then you do the exact same thing you'd do for a .NET object graph that's expensive: GC.Collect() helps that along. 如果您知道COM对象具有异常高的资源使用率,并且需要确定性地释放它,那么您执行与为昂贵的.NET对象图执行完全相同的操作:GC.Collect()有助于此。 Check this answer for the reason why Marshal.ReleaseComObject() tends to be used unnecessarily. 检查这个答案是因为Marshal.ReleaseComObject()往往被不必要地使用。

Here's sample code with which I managed to reproduce the behavior in Hans Passant's answer. 这是我设法重现Hans Passant答案中的行为的示例代码。 I can click on button 1 to create an object, and then when I click on button 2 to access it after the creating thread has terminated, I get the error "COM object that has been separated from its underlying RCW cannot be used." 我可以单击按钮1来创建一个对象,然后当我在创建线程终止后单击按钮2来访问它时,我得到错误“已经与其底层RCW分离的COM对象无法使用”。

Public Class Form1

   Dim comRef As Microsoft.Office.Interop.Outlook.Application

   Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      Dim t As New System.Threading.Thread(AddressOf CreateApplication)
      t.SetApartmentState(Threading.ApartmentState.STA)
      t.Start()
   End Sub

   Private Sub CreateApplication()
      comRef = New Microsoft.Office.Interop.Outlook.Application
   End Sub 

   Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
      TextBox1.Text = comRef.DefaultProfileName
   End Sub
End Class

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM