在多线程环境中处置COM

时间:2011-07-13 20:14:55

标签: c# multithreading com dispose idisposable

我正在尝试正确处理由Visual Studio生成的RCW中包含的旧版VFP(FoxPro)COM控件。控件公开了一个Destroy方法,我应该调用它来让控件正确地撕下来。当请求处理COM实例时,很有可能在后台线程上执行控件上的方法。 VFP是单线程单元模型,因此在调用Destroy时,它应该只添加到VFP执行堆栈中。

调用Destroy理想情况下是正确的做法,因为它允许COM实例清理一些资源。我担心的是,实例化VFP COM控件实际上启动了托管控件的VFP语言运行时实例,并且该实例可能被锁定(无响应)。这个COM组件在一个大型企业规模的20年遗留应用程序中公开功能,我看到.NET线程试图在此控件上调用方法的情况只是阻塞而不会抛出错误(总是由遗留的错误引起) VFP代码)。这种情况不会经常发生,但它通常足以促使我构建一个实例管理器,在后台线程中运行VFP COM实例上的方法,并定期检查该线程是否被阻塞,如果是,则销毁COM实例和线程并重新启动要监视的新实例。

这是处理后台方法可能正在执行的线程的正确方法吗?

我是否应该通过尝试调用Destroy方法以允许COM控件正确拆卸来尝试获得更高级的效果?

if (_vfpThread != null)
{
  try
  {
    if (_vfpThread.IsAlive)
      _vfpThread.Abort();
  }
  catch (ThreadAbortException)
  { }
  finally
  {
    _vfpThread = null;
  }
}

if (_vfpInstance != null)
{
  Marshal.ReleaseComObject(_vfpInstance);
  _vfpInstance = null;
}

2 个答案:

答案 0 :(得分:4)

当基于VFP的COM对象(总是在STA公寓中运行)上的方法调用挂起时,从另一个线程调用该相同COM对象上的任何方法将阻塞,直到前一个调用返回(退出公寓)。 这意味着,任何试图同时调用Destroy()的线程都将受到第一个线程的支配。如果该线程不知道自愿退出,理论上可以保持处置线程无限期地被阻止。因此,换句话说,没有直接方法通过从另一个线程中调用COM对象上的另一个方法来要求第一个线程立即退出该方法。调用_vfpThread.Abort()应该可行,但这种方法的安全性在很大程度上取决于VFP类的内部。 在许多情况下,由于它是遗留代码,它不会有像try / catch / finally部分那样允许优雅退出的东西,因此资源可能最终未被释放。 - 坏!

另一种方法是在某处设置一个外部标志(注册表,文件,等等),这些标志可供第一个线程在其执行的方法中读取。当然,这要求VFP类知道必须从每个COM发布的方法中读取标志,并相应地并且快速地采取行动。

另外,关于您的代码段。 在正在中止线程的代码中捕获ThreadAbortException仅在执行此代码的线程正在中止时才有意义。这将是非常尴尬的,因为它可以只从方法返回。 (或者,这个调用_vfpThread.Abort()的线程是否也可能从另一个线程中止?) 在正常情况下,您需要包含在ThreadAbortException捕获器中的是第一个线程的主代码,它对COM对象上的所有这些业务方法执行调用。 理想情况下,您可以像在VFP方法本身一样深入堆栈,在重新抛出异常之前,代码可以优雅地关闭所有资源/表等。 然后在传递给ThreadStart的main方法中,你有一个类似的捕获器,除非它从方法中安静地返回,从而终止线程或将它释放到线程池。

答案 1 :(得分:0)

是的,我确实理解了你的代码.-谢谢。

如果vfp线程在60秒内没有正常退出,则中止它可能是您唯一能做的事情。 就Dispose应该做什么而言 - 它应该尽力释放所有非托管资源,不幸的是,这些资源在VFP COM类中使用/打开时会隐藏在此代码中。因此,如果占用COM对象,主线程将无法强制它释放这些资源。也许你可以尝试做的是将COM业务方法的整个主体包装在VFP try-catch块中,并在catch部分释放资源/关闭表。 try-catch块很可能会捕获从主线程调用_vfpThread.Abort()导致的ThreadAbortException。