如何保护可能在多线程或异步环境中使用的资源?

时间:2014-01-09 03:36:31

标签: c# multithreading asynchronous synchronization async-await

我正在研究各种消费者使用的C#API。此API提供对共享资源的访问(在我的情况下,用于执行串行通信的硬件),通常会有一些不同的参与者尝试同时使用它。

我遇到的问题是我的一些消费者希望在多线程环境中使用它 - 每个actor都独立工作并尝试使用该资源。一个简单的锁在这里工作正常。但我的一些消费者更愿意使用async-await和时间片资源。 (据我所知),这需要一个异步锁定来将时间片返回到其他任务;在锁定时阻塞将停止整个线程。

我认为拥有串行锁定最多是无法执行的,并且最坏的情况是潜在的竞争条件或死锁。

那么我如何在公共代码库中为这两种潜在的并发使用保护这个共享资源呢?

1 个答案:

答案 0 :(得分:29)

您可以将SemaphoreSlim与1一起用作请求数。 SemaphoreSlim允许使用WaitAsync和旧同步方式锁定async方式:

await _semphore.WaitAsync()
try
{
    ... use shared resource.
}
finally
{
    _semphore.Release()
}

您还可以根据Stephen Toub的精彩帖子Building Async Coordination Primitives, Part 6: AsyncLock撰写自己的AsyncLock。我在我的应用程序中完成了它,并允许在同一个构造上进行同步和异步锁定。

用法:

// Async
using (await _asyncLock.LockAsync())
{
    ... use shared resource.
}

// Synchronous
using (_asyncLock.Lock())
{
    ... use shared resource.
}

实现:

class AsyncLock
{
    private readonly Task<IDisposable> _releaserTask;
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
    private readonly IDisposable _releaser;

    public AsyncLock()
    {
        _releaser = new Releaser(_semaphore);
        _releaserTask = Task.FromResult(_releaser);
    }
    public IDisposable Lock()
    {
        _semaphore.Wait();
        return _releaser;
    }
    public Task<IDisposable> LockAsync()
    {
        var waitTask = _semaphore.WaitAsync();
        return waitTask.IsCompleted
            ? _releaserTask
            : waitTask.ContinueWith(
                (_, releaser) => (IDisposable) releaser,
                _releaser,
                CancellationToken.None,
                TaskContinuationOptions.ExecuteSynchronously,
                TaskScheduler.Default);
    }
    private class Releaser : IDisposable
    {
        private readonly SemaphoreSlim _semaphore;
        public Releaser(SemaphoreSlim semaphore)
        {
            _semaphore = semaphore;
        }
        public void Dispose()
        {
            _semaphore.Release();
        }
    }
}