Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 处理多个应用程序使用的库中非托管资源的优雅方法_C#_Singleton_Unmanaged_Idisposable - Fatal编程技术网

C# 处理多个应用程序使用的库中非托管资源的优雅方法

C# 处理多个应用程序使用的库中非托管资源的优雅方法,c#,singleton,unmanaged,idisposable,C#,Singleton,Unmanaged,Idisposable,我的应用程序中的库需要全局使用非托管资源 为此,图书馆提供了以下服务: 有一个单例类继承自IDisposable。 非托管资源的获取发生在类的Instance()方法中。 资源总是通过MySingleton对象访问,因此这似乎是确保非托管资源在需要时可用的一种合乎逻辑的方式 public class MySingleton : IDisposable { private static MySingleton instance; public static MySingleton

我的应用程序中的库需要全局使用非托管资源

为此,图书馆提供了以下服务:

  • 有一个单例类继承自
    IDisposable
  • 非托管资源的获取发生在类的
    Instance()
    方法中。
  • 资源总是通过
    MySingleton
    对象访问,因此这似乎是确保非托管资源在需要时可用的一种合乎逻辑的方式

    public class MySingleton : IDisposable
    {
        private static MySingleton instance;
    
        public static MySingleton Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (typeof(MySingleton))
                    {
                        if (instance == null)
                        {
                            instance = new MySingleton();
    
                            // Acquire unmanaged resource here
                        }
                    }
                }
    
                return instance;
            }
        }
    
        public void Dispose()
        {
            // Release unmanaged resource here
        }
    }
    
    问题
    • 具有上述单例的库正由多个应用程序使用
    • 为了确保正确清理非托管资源,我需要在每个应用程序中调用
      MySingleton.Instance.Dispose()
      (通常在
      finally
      块中,以确保即使在
      异常
      情况下也会发生这种情况)
    • 如果创建了另一个应用程序(即入口点),而作者忘记调用
      Dispose
      方法,则可能会导致未被妥善清理的非托管资源
    我尝试向
    MySingleton
    类添加一个析构函数来执行此操作,但在应用程序退出时似乎没有命中其中的断点。我猜这是因为GC是不确定的,可能应用程序在静态对象的析构函数被销毁之前就结束了,但我不确定


    有没有一种优雅的方法可以确保始终清理此资源,而不依赖客户端应用程序显式调用
    Dispose
    来确保这种情况发生?

    关于您的析构函数没有被命中,请看这里: 虽然调用了析构函数,但我认为在调试时您看不到这一点。 对于您的单例,很明显,它在程序退出之前不会被调用,因为它是静态的


    关于确保调用Dispose,我认为最好的方法是继承IDisposable并记录Dispose的需要。如果有一种自动处理东西的方法,您不认为MS会对SqlConnection或任何其他一次性类这样做吗?顺便说一句,我认为确定何时在类本身中处理实例不是一个好的做法。这样,您就限制了类的使用,并且无法知道将使用您的类的其他组件想要做什么。

    使用单例实现IDisposable听起来是错误的。谁负责处理该物品

    当两个线程或两个进程试图访问资源时,应该发生什么?失败者应该阻止还是抛出异常

    我认为您最好采用RAII方法,在这种方法中,您有一个实现IDisposable的类,并且该类仅在资源使用时有效:

    class MyResource : IDisposable { 
      public MyResource() {
        // Acquire resource here
      }
    
      public void Dispose() {
        // Free resource here, along with extra stuff to attempt
        // to catch situations where its not disposed 
      }
    }
    
    然后:


    您不能保证使用库的每个其他开发人员都会记得正确地处理该类,但是任何未能在using语句中使用该类的人都可以通过一根锋利的大棒进行教育。

    您对singleton的静态引用将使它保持活动状态,而不管其他人是否实际使用它。如果您希望检测到对您的单身汉的所有外部引用何时消失,而您自己却无法使其保持活动状态,则必须执行以下操作之一:

  • 为singleton保留一个“WeakReference”,并在每次有人请求对象实例时返回该引用。您可能希望保留一个长的和一个短的“WeakReference”;一旦某个对象符合最终确定的条件,短的一个将立即失效;在对象最终确定后的GC循环之前,长值将保持有效。如果短的“WeakReference”无效,则可能需要创建一个新实例,即使您有一个长的弱引用,即使终结器尚未运行。另一方面,您可能需要一个长的“WeakReference”来帮助处理在旧对象上请求终结器和它实际运行之间请求新对象的情况。
  • 对“真正的”单身汉保持强烈的引用,但绝不向任何人透露。相反,创建一个包装器并存储一个“WeakReference”。包装器的终结器应该请求单例清理自己。这种方法可能比第一种方法更安全,因为在这种情况下,代码会在旧对象符合终止条件和实际发生终止之间请求单例实例。如果所有清理都是通过单个“Finalize”方法进行的,则get singleton方法可能会请求终结器跳过清理,并知道它是否及时发出了请求(如果没有,则可能需要等待终结器完成,然后再创建新实例)。
  • 保持对“真实”单例的强引用,并为每个请求它的不同实体提供不同的包装器。保留一个包装器尚未调用“finalize”的列表(最多保留对包装器本身的弱引用,但保留对其相关信息的强引用),当列表为空时,执行清理。这种方法往往比第二种更复杂,但它可以确定谁持有对对象的引用。 如果您的资源是一个旧实例存在时无法重新创建的资源,我的建议通常是方法#2。方法#1的一个问题是,如果有多个可终结对象与一个资源关联,则可能会有一段相当长的时间间隔,在此期间资源会被部分清除,因此代码可能别无选择,只能等待其他对象的终结器完成。使用方法#2时,如果在终结器挂起时请求单例,则请求将很快到来,以阻止终结器执行清理(在这种情况下,它不需要等待终结器运行,因为终结器已挂起)
    using( var r = new MyResource() ) {
      // Do work here.
    }