管理IDisposable对象的共享所有权的问题

时间:2017-05-24 04:28:38

标签: c# .net shared-ptr idisposable

[有一个类似的问题Shared ownership of IDisposable objects in C#但是接受的答案会遇到我的解决方案所存在的类似设计问题]

我一直在努力思考这个问题,因为在c#中工作时,许多设计对垃圾收集器采取了盲目的信念。原因很多时候很容易被忽略,需要处理IDisposable而不是小心地使它们等同于c ++中的资源分配。我理解,迂腐地说Dispose并不等同于c++ destructor,但如果你坚持需要以确定性方式清理的本地资源,那就变得非常相似了。

但是如果对象由多个资源共享(例如,一个HttpClient对象,它意味着要创建并同时用于执行),谁有责任调用Dispose,因为有没有单身老板?为了解决这个问题,我提出了一个SharedOwner库,其界面与shared_ptr类似。

代码段:

SharedOwner:

public static SharedHandle<T> MakeSharedHandle(Func<T> createSharedOwner)
{
    var obj = new SharedHandle<T>(createSharedOwner());
    return obj;
}

public SharedHandle(T obj)
{
    AddReference();
    m_object = obj;
}

public Handle<T> GetHandle()
{
    AddReference();
    return new Handle<T>(this);
}

internal void Decrement()
{
    if (Interlocked.Decrement(ref m_refCounter) == 0)
    {
        m_object.Dispose();
        m_object = default(T);
    }
}

internal T GetInternalHandler() { return m_object; }

private void AddReference()
{
    Interlocked.Increment(ref m_refCounter);
}

~SharedHandle()
{
    m_object.Dispose();
}

Handle

是一个透明的包装器,它管理引用从消费者那里抽象的引用计数调用。

public sealed class Handle<T> : IDisposable where T : IDisposable
    {
        private SharedHandle<T> m_handle;
        bool disposed = false;
        internal Handle(SharedHandle<T> handle) { m_handle = handle; }

        public void Dispose()
        {
            if (!disposed)
            {
                m_handle.Decrement();
                GC.SuppressFinalize(this);
                disposed = true;
            }
        }

        ~Handle()
        {
            if (!disposed)
            {
                m_handle.Decrement();
            }
        }

我想象的典型使用模式是:

using (var sharedClient = this.m_sharedClient.GetHandle()) // m_sharedClient is the SharedHandle passed
{
     var httpClient = (HttpClient)sharedClient;
     // use httpClient
}

现在我看到这种方法存在两个问题,这与模拟shared_ptr行为的原始动机有所不同:

  • 第一个引用由SharedHandle本身持有。因此,即使所有句柄都超出范围,一个引用仍将由SharedHandle保留,从而使得Decrement中的if块无法访问。

  • 最后的Dispose发生在SharedHandle死亡时,在很多意义上并不比不在底层对象本身上调用Dispose更好。因此,使解决方案的价值更低。

我正在考虑将引用计数移动到句柄并在SharedHandle中使用shared_ptr作为控制块,但这意味着最终可能会有效{ {1}}对象持有Disposed内部对象。

我能想到的另一个选择是SharedHandle来自SharedHandle,只需在IDisposable中调用Decrement。但随后这带来了其他一系列设计问题。有没有什么可以以更优雅的方式解决这个问题?

0 个答案:

没有答案