线程安全的Dispose方法?

时间:2011-02-17 03:52:14

标签: .net thread-safety dispose idisposable

MSDN很好地记录了BCL类型的实例成员的线程安全性,但我从未真正看到过指示如何调用Dispose类型的IDisposable方法的信息。

Dispose方法a)是否保证对所有类都是线程安全的,b)从不保证是线程安全的,c)保证对某些类是线程安全的(如果是,那么在哪里这具体记录了吗?

最后,如果保证Dispose方法是线程安全的,那么这是否意味着我必须在使用可支配资源的类中对每个实例方法进行锁定?

侧点:我知道类型的终结器应该是线程安全的,因为.NET中的垃圾收集方式(非常积极),并且它们可能会调用Dispose方法。但是,让我们暂时搁置这个问题。

3 个答案:

答案 0 :(得分:6)

线程安全和Dispose的问题有点棘手。因为在很多情况下,任何一个线程开始处理对象时,任何线程都可以合法地对某个对象进行处理是尝试自己处理它,它会乍一看似乎是确保线程安全的唯一必要条件。在'dispos'标志上使用Interlocked.Exchange以确保一个线程的Dispose尝试发生,另一个被静默忽略。实际上,这是一个很好的起点,我认为它应该是标准Dispose模式的一部分(CompareExchange应该在密封的基类包装器方法中完成,以避免每个派生类都需要使用它自己的私有处置国旗)。不幸的是,如果考虑到Dispose实际上做了什么,事情要复杂得多。

Dispose的真正目的不是对被处理的对象做某事,而是清理该对象持有引用的其他实体。这些实体可以是托管对象,系统对象或其他完全不同的东西;它们甚至可能与处理的物体不在同一台计算机上。为了使Dispose成为线程安全的,那些其他实体会允许Dispose在其他线程可能正在使用它们做其他事情的同时清理它们。有些对象可以处理这种用法;别人不能。

一个特别令人烦恼的例子:允许对象使用不是线程安全的RemoveHandler方法的事件。因此,任何清理事件处理程序的Dispose方法只能从与创建订阅的线程相同的线程中调用。

答案 1 :(得分:2)

MSDN here上的页面实际上从未明确说明Dispose方法不是线程安全的,但是对于我的阅读,他们的代码暗示不,它们不是线程安全的,如果需要你需要考虑它。

特别是示例代码中的注释:

// This class shows how to use a disposable resource.
// The resource is first initialized and passed to
// the constructor, but it could also be
// initialized in the constructor.
// The lifetime of the resource does not 
// exceed the lifetime of this instance.
// This type does not need a finalizer because it does not
// directly create a native resource like a file handle
// or memory in the unmanaged heap.

public class DisposableResource : IDisposable
{

    private Stream _resource;  
    private bool _disposed;

    // The stream passed to the constructor 
    // must be readable and not null.
    public DisposableResource(Stream stream)
    {
        if (stream == null)
            throw new ArgumentNullException("Stream in null.");
        if (!stream.CanRead)
            throw new ArgumentException("Stream must be readable.");

        _resource = stream;

        _disposed = false;
    }

    // Demonstrates using the resource. 
    // It must not be already disposed.
    public void DoSomethingWithResource() {
        if (_disposed)
            throw new ObjectDisposedException("Resource was disposed.");

        // Show the number of bytes.
        int numBytes = (int) _resource.Length;
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString());
    }


    public void Dispose() 
    {
        Dispose(true);

        // Use SupressFinalize in case a subclass
        // of this type implements a finalizer.
        GC.SuppressFinalize(this);      
    }

    protected virtual void Dispose(bool disposing)
    {
        // If you need thread safety, use a lock around these 
        // operations, as well as in your methods that use the resource.
        if (!_disposed)
        {
            if (disposing) {
                if (_resource != null)
                    _resource.Dispose();
                    Console.WriteLine("Object disposed.");
            }

            // Indicate that the instance has been disposed.
            _resource = null;
            _disposed = true;   
        }
    }
}

答案 2 :(得分:2)

我相当确定,除非另有说明,否则任何类的Dispose()方法都将被视为“实例成员”,用于指示线程安全性的文档。

因此,如果文档声明实例成员不是线程安全的,那么Dispose()也不一定是,除非特别指出它与其他成员不同。