多线程应用中静态全局非托管资源的管理

Posted

技术标签:

【中文标题】多线程应用中静态全局非托管资源的管理【英文标题】:Management of Static Global Unmanaged Resources in Multithreaded App 【发布时间】:2012-02-13 14:44:30 【问题描述】:

我们遇到了一个资源管理问题,我们已经为此苦苦挣扎了几个星期,虽然我们终于找到了解决方案,但对我来说仍然很奇怪。

我们针对旧系统开发了大量互操作代码,这些系统公开了 C API。该系统的许多特点之一是(出于未知原因),“环境”似乎是进程范围的,必须在使用 API 之前进行初始化。但是,它只能初始化一次,并且在完成后必须“关闭”。

我们最初使用单例模式来实现这一点,但是当我们在 IIS 托管的 Web 服务中使用这个系统时,我们的 AppDomain 偶尔会被回收,从而导致“孤立”环境泄漏内存。由于最终确定和(显然)甚至 IIS 回收都是不确定的,并且在所有情况下都难以检测到,因此我们已切换到似乎运行良好的处置+引用计数模式。但是,手动进行引用计数感觉很奇怪,我相信有更好的方法。

对于在这样的环境中管理静态全局一次性资源有什么想法吗?

这是环境管理的粗略结构:

public class FooEnvironment : IDisposable

  private bool _disposed;
  private static volatile int _referenceCount;
  private static readonly object InitializationLock = new object();

  public FooEnvironment()
  
    lock(InitilizationLock)
    
      if(_referenceCount == 0)
      
        SafeNativeMethods.InitFoo();
        _referenceCount++;
      
    
  

  public void Dispose()
  
    if(_disposed)
      return;

    lock(InitilizationLock)
    
      _referenceCount--;
      if(_referenceCount == 0)
      
        SafeNativeMethods.TermFoo();
      
    

    _disposed = true;
  


public class FooItem

  public void DoSomething()
  
    using(new FooEnvironment())
    
      // environment is now initialized (count == 1)
      NativeMethods.DoSomething();

      // superfluous here but for our purposes...
      using(new FooEnvironment())
      
        // environment is initialized (count == 2)
        NativeMethods.DoSomethingElse();
      
      // environment is initialized (count == 1)
    
    // environment is unloaded
  

【问题讨论】:

【参考方案1】:

我在这里首先跳脚,因为对于您的特定代码库有很多未知数,但我想知道基于会话的方法是否有任何里程?您可以拥有一个(线程安全)会话工厂单例,负责确保仅初始化一个环境,并通过将其绑定到 ASP.NET AppDomain 和/或类似事件上的事件来适当地处置该环境。您需要将此会话模型烘焙到您的 API 中,以便所有客户端在进行任何调用之前首先建立一个会话。为这个答案的模糊性道歉。如果您可以提供一些示例代码,也许我可以给出更具体/详细的答案。

【讨论】:

这是一个合理的建议。我想我们正试图避免会话,以使每个呼叫尽可能无状态。我认为当会话意外终止时,我们很可能会遇到类似的问题。我很抱歉没有发布代码,但是发布代码是一个棘手的问题...... 如何使用 AOP 对 API 隐藏会话管理,从而使其看起来无状态?您可以拦截所有公共 API 调用,以确保在继续调用之前初始化环境。同样,将进程的生命周期绑定到客户端 AppDomain 的生命周期。我在这里抓住了稻草,但那是我的两便士价值。【参考方案2】:

您可能要考虑的一种方法是为您的非托管组件创建一个隔离的 AppDomain。这样,当 IIS 托管的 AppDomain 被回收时,它就不会成为孤立对象。

【讨论】:

有趣...为什么 IIS 不也回收这个域? 我的理解是你可以回收一个 ASP.NET 应用程序(这是一个 AppDomain),例如通过“触摸” web.config 或 bin 文件夹中的文件。如果发生这种情况,我相信同一进程中的其他 AppDomain(例如其他 ASP.NET 应用程序)将继续运行。另一方面,如果应用程序池(IIS6 及更高版本)被回收,工作进程将被关闭并重新启动。您描述的问题表明该进程没有被回收,只有 AppDomain。

以上是关于多线程应用中静态全局非托管资源的管理的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 非托管 C++ 智能文件资源管理器

利用IDisposable接口构建包含非托管资源对象

托管和非托管

非托管 vc++ 静态库和 C# GUI 应用程序

.net中的托管资源和非托管资源

在 C# 应用程序退出时清理非托管 C++ 线程