.net 处理方法的线程安全性?

niknxzdl  于 2023-07-01  发布在  .NET
关注(0)|答案(4)|浏览(156)

MSDN很好地记录了BCL类型的示例成员的线程安全性,但我从未真正看到过如何调用IDisposable类型的Dispose方法的信息。
Dispose方法是否a)保证对所有类都是线程安全的,b)从不保证是线程安全的,c)保证对某些类是线程安全的(如果是,在哪里有专门的文档说明)?
最后,如果Dispose方法被保证是线程安全的,这是否意味着我必须在类中使用一次性资源的每个示例方法周围放置一个锁?
附带说明:我知道类型的终结器应该是线程安全的,因为垃圾收集在.NET中的工作方式(相当激进),它们可能会调用Dispose方法。但是,让我们把这个问题放在这里。

holgip5t

holgip5t1#

线程安全和Dispose的问题有些棘手。由于在许多情况下,一旦任何其他线程开始释放对象,任何线程可以合法地对对象执行的唯一事情就是尝试自己Dispose,因此乍一看,确保线程安全的唯一必要的事情似乎是在'dispose'标志上使用Interlocked.Exchange,以确保一个线程的Dispose尝试发生,而另一个线程被默默忽略。事实上,这是一个很好的起点,我认为它应该是标准Dispose模式的一部分(CompareExchange应该在密封的基类 Package 器方法中完成,以避免每个派生类都需要使用自己的私有dispose标志)。不幸的是,如果考虑Dispose实际上做了什么,事情就复杂得多了。
Dispose的真实的目的不是对被释放的对象做什么,而是清理该对象持有引用的其他实体。这些实体可以是被管理对象、系统对象或完全其他的东西;它们甚至可能不在与被处理的对象相同的计算机上。为了使Dispose是线程安全的,那些其他实体将允许Dispose在其他线程可能对它们做其他事情的同时清理它们。有些对象可以处理这种用法;其他人不能。
一个特别恼人的例子:允许对象具有带有非线程安全的RemoveHandler方法的事件。因此,任何清理事件处理程序的Dispose方法都只能从创建订阅的线程所在的线程调用。

r7s23pms

r7s23pms2#

MSDN上的页面实际上从未明确指出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;   
        }
    }
}
gzszwxb4

gzszwxb43#

我相当肯定,除非另有说明,否则任何类的Dispose()方法都将被视为“示例成员”,以便在文档中指示线程安全与否。
因此,如果文档声明示例成员不是线程安全的,那么Dispose()也不一定是线程安全的,除非特别指出它与其他成员不同。

cig3rfwq

cig3rfwq4#

Dispose只是一个方法,没有什么特别的。它的特殊用例是using语句-所以你不必记住写
try { do something with object} finally { object.Dispose();}
所以Dispose不是线程安全的--为什么它应该是唯一调用它的线程应该是拥有它的线程。
永远不要从多个线程中对单个对象调用Dispose。虽然Dispose可能不是线程安全的,但如果你有多个线程在处理同一个对象,那么你就有了麻烦。
不要在多个线程上释放单个对象。这不仅是一个反模式(谁拥有对象?)而是设计上的错误。
如果您使用Interlocked.Exchange来同步释放,那么您就丢失了对象所有者是谁。

相关问题