我遇到了线程和处理资源的问题。
我有一个C#Windows Forms应用程序,它在一个线程中运行昂贵的操作。该线程实例化ActiveX控件(AxControl)。必须处理此控件,因为它使用大量内存。所以我实现了Dispose()方法甚至是析构函数。
线程结束后,调用析构函数。 UI线程遗憾地调用了这一点。所以调用activexControl.Dispose();失败并显示消息“已与其基础RCW分离的COM对象”,因为该对象属于另一个线程。
如何正确地做到这一点,还是我使用的设计不好?
(我将代码删除到最低限度,包括消除任何安全问题。)
class Program
{
[STAThread]
static void Main()
{
// do stuff here, e.g. open a form
new Thread(new ThreadStart(RunStuff);
// do more stuff
}
private void RunStuff()
{
DoStuff stuff = new DoStuff();
stuff.PerformStuff();
}
}
class DoStuff : IDisposable
{
private AxControl activexControl;
DoStuff()
{
activexControl = new AxControl();
activexControl.CreateControl(); // force instance
}
~DoStuff()
{
Dispose();
}
public void Dispose()
{
activexControl.Dispose();
}
public void PerformStuff()
{
// invent perpetuum mobile here, takes time
}
}
答案 0 :(得分:2)
我不清楚实现Dispose方法的ActiveX控件的含义。 IDisposable
模式适用于托管代码。要释放COM对象,通常会使用Marshal.ReleaseComObject
- 也许你正在AxControl
类中执行此操作,而您的实现没有显示。
上述代码存在一些问题。
您应该处置IDisposable DoStuff
实例:
private void RunStuff()
{
using (DoStuff stuff = new DoStuff())
{
stuff.PerformStuff();
}
}
您不应该在终结器中访问托管资源 - 在您的情况下,终结器调用Dispose,然后引用托管的axControl实例。在终结器运行时,可能已经收集了此实例。
由于您没有直接在DoStuff类中使用非托管资源,因此您可能不需要终结器,但如果您有,请遵循MSDN上的标准IDisposable模式,并且不要尝试处置任何托管对象
<强>更新强>
注释:
AxControl是由Visual Studio生成的.NET Interop Wrapper DLL。
在这种情况下,什么是Dispose()方法?我不明白为什么你会在ActiveX控件中实现这样的方法,它具有确定性的最终化 - 通常你会在最后一个COM引用被释放时进行清理。
您的DoStuff.Dispose
方法可能想要释放COM对象,例如
public void Dispose()
{
activexControl.Dispose();
Marshal.ReleaseComObject(activexControl);
}
答案 1 :(得分:1)
为什么不让工作线程明确处理它?</ p>
您可以更改
DoStuff stuff = new DoStuff();
stuff.PerformStuff();
到
using(DoStuff stuff = new DoStuff())
{
stuff.PerformStuff();
}
所以你甚至不用担心它。
答案 2 :(得分:0)
我正在处理由第三方提供的活动x控件,因此无法更改ActiveX内部的处理例程。
我相信你是正确的,“已经与其基础RCW分离的COM对象”问题是一个线程问题,而不是一个处置实现问题。 COM与托管线程和托管内存不相容。
尝试这样的事情:
// Check to see if we need to use Invoke before wasting time with it
if (activexControl.InvokeRequired)
{
// invoke Dispose on the control's native thread to avoid the COM exception
activexControl.Invoke(() => activexControl.Dispose());
}
else
{
// run dispose on this thread
activexControl.Dispose();
}
调用是避免COM错误所需的全部内容,if语句在不需要调用的情况下有助于提高性能。
请注意,创建活动x控件的线程仍需要活着,否则您将遇到不同的问题。在您生成新线程之后,在您的精简代码主出口中,当您稍后需要它时它将不可能存活。我一直在单元测试中遇到这种情况,并且必须在退出我的顶级函数之前插入等待其他线程结束的阻塞调用。