小编典典

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

c#

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

我遇到的问题是,我的一些消费者希望在多线程环境中使用它-
每个参与者都独立工作并尝试使用资源。一个简单的锁在这里可以正常工作。但是我的一些消费者更喜欢使用异步等待和时间切片资源。(据我了解),这需要异步锁定才能将时间片返回给其他任务;在锁处阻塞会中断整个线程。

而且我认为拥有串行锁最多只能表现不佳,而潜在的竞争状况或最坏的情况就是死锁。

那么,如何针对两种潜在的并发用法在公共代码库中保护此共享资源?


阅读 316

收藏
2020-05-19

共1个答案

小编典典

您可以使用SemaphoreSlim1作为请求数。SemaphoreSlim允许async使用WaitAsync和同步方式锁定:

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

您还可以AsyncLock根据Stephen
Toub的出色文章《构建异步协调基元》第6部分: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();
        }
    }
}
2020-05-19