在多线程环境中处理COM

本文关键字:处理 COM 环境 多线程 | 更新日期: 2023-09-27 18:01:54

我正试图正确处理一个遗留的VFP (FoxPro) COM控件,该控件封装在Visual Studio生成的RCW中。该控件公开了一个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;
}

在多线程环境中处理COM

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

另一种方法是在某个地方设置一个外部标志(注册表、文件等),这将供第一个线程从它正在执行的方法中读取。这当然要求VFP类意识到必须从它的每个com发布的方法中读取标志,并相应地快速地采取行动。

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

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

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