在等待挂起的操作取消时处置SemaphoreSlim是否安全? [英] Is it safe to dispose a SemaphoreSlim while waiting for pending operations to cancel?

查看:38
本文介绍了在等待挂起的操作取消时处置SemaphoreSlim是否安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不得不使用SemaphoreSlim来确保以单线程方式访问我的代码的某些部分,并希望确保我正确地处理了所有内容。假设我有以下类:

public class Foo
{
    private readonly CancellationTokenSource _canceller = new CancellationTokenSource();
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);

    ~Foo()
    {
        Dispose(false);
        GC.SuppressFinalize(this);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        _canceller.Cancel();

        if (_disposing)
            _semaphore.Dispose();

        _disposed = true;
    }

    public async Task ExecuteAsync()
    {
        try
        {
            await _semaphore.WaitAsync(_canceller.Token);
        }
        catch (OperationCanceledException)
        {
            throw new ObjectDisposedException(nameof(Foo), "Cannot execute on a Foo after it has been disposed");
        }
        
        try
        {
            // Critical section
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

当我调用Dispose(true)时,我实际上是一个接一个地执行以下行:

_canceller.Cancel();
_semaphore.Dispose();
我的问题是,当前等待临界区的任何其他线程/任务会发生什么情况?我是否可以保证他们总是首先看到取消,因此不会在处理信号量时出现问题?到目前为止,这还没有引起任何问题,但这并不意味着它是安全的。

推荐答案

documentation for SemaphoreSlim似乎对您的问题非常具体。

SemaphoreSlim的所有公共成员和受保护成员都是线程安全的,并且可以从多个线程并发使用,Dispose()除外,它必须在SemaphoreSlim上的所有其他操作完成后才能使用。

确保所有其他操作都已完成的唯一真正方法是确保所有任务都已完成,如文档中的示例所示。

public class Foo
{
    private readonly List<Task> _semaphoreTasks = new list<Task>();

    protected void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        _canceller.Cancel();

        if (_disposing) 
        {
            Task.WaitAll(tasks);
            _semaphore.Dispose();
        }

        _disposed = true;
    }

    public async Task ExecuteAsync()
    {
        try
        {
            var task = _semaphore.WaitAsync(_canceller.Token);
            _semaphoreTasks.Add(task);
            await task;
        // ...

这很可能还需要确保在Dispose执行时将一些内容添加到列表中。(您可以将私有类变量设置为本地方法变量,然后将类变量设置为空)

这篇关于在等待挂起的操作取消时处置SemaphoreSlim是否安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆