l0n*_*ley 26 c# asynchronous locking
我有一些问题ReaderWriterLockSlim.我无法理解它是如何运作的.
我的代码:
private async Task LoadIndex()
{
if (!File.Exists(FileName + ".index.txt"))
{
return;
}
_indexLock.EnterWriteLock();// <1>
_index.Clear();
using (TextReader index = File.OpenText(FileName + ".index.txt"))
{
string s;
while (null != (s = await index.ReadLineAsync()))
{
var ss = s.Split(':');
_index.Add(ss[0], Convert.ToInt64(ss[1]));
}
}
_indexLock.ExitWriteLock();<2>
}
Run Code Online (Sandbox Code Playgroud)
当我在输入写锁定<1>,在调试器我可以看到_indexLock.IsWriteLockHeld是true,但是当执行步骤<2>我看_indexLock.IsWriteLockHeld是false
和_indexLock.ExitWriteLock抛出异常SynchronizationLockException处理消息"写锁定而不被保持被释放".我做错了什么?
Ste*_*ary 44
ReaderWriterLockSlim是一个线程仿射锁类型,因此它通常不能与async和一起使用await.
您应该使用SemaphoreSlim带WaitAsync,或者(如果你真的需要一个读/写器锁),用我AsyncReaderWriterLock从AsyncEx或斯蒂芬Toub的AsyncReaderWriterLock.
您可以使用可靠且轻量级的方法安全地模拟读取器/写入器锁定机制,并保留/SemaphoreSlim的优点。创建一个可用锁的数量,该锁的数量等于锁定资源以同时读取的例程数量。每个人都会像往常一样请求一把锁。对于您的写入例程,请确保它在执行操作之前请求所有可用的锁。这样,您的写作例程将始终单独运行,而您的阅读例程可能仅在它们之间共享资源。例如,假设您有 2 个阅读例程和 1 个写作例程。asyncawaitSemaphoreSlim
SemaphoreSlim semaphore = new SemaphoreSlim(2);
async void Reader1()
{
await semaphore.WaitAsync();
try
{
// ... reading stuff ...
}
finally
{
semaphore.Release();
}
}
async void Reader2()
{
await semaphore.WaitAsync();
try
{
// ... reading other stuff ...
}
finally
{
semaphore.Release();
}
}
async void ExclusiveWriter()
{
// the exclusive writer must request all locks
// to make sure the readers don't have any of them
// (I wish we could specify the number of locks
// instead of spamming multiple calls!)
await semaphore.WaitAsync();
await semaphore.WaitAsync();
try
{
// ... writing stuff ...
}
finally
{
// release all locks here
semaphore.Release(2);
// (oh here we don't need multiple calls, how about that)
}
}
Run Code Online (Sandbox Code Playgroud)
显然,只有当您事先知道可以同时运行多少个阅读例程时,此方法才有效。不可否认,太多会使这段代码变得非常难看。
前段时间我为我的项目实现了基于两个SemaphoreSlim的AsyncReaderWriterLock类。希望它能有所帮助。它实现了相同的逻辑(多个读取器和单个写入器),同时支持异步/等待模式。当然,它不支持递归,并且无法防止错误使用:
var rwLock = new AsyncReaderWriterLock();
await rwLock.AcquireReaderLock();
try
{
// ... reading ...
}
finally
{
rwLock.ReleaseReaderLock();
}
await rwLock.AcquireWriterLock();
try
{
// ... writing ...
}
finally
{
rwLock.ReleaseWriterLock();
}
Run Code Online (Sandbox Code Playgroud)
public sealed class AsyncReaderWriterLock : IDisposable
{
private readonly SemaphoreSlim _readSemaphore = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim _writeSemaphore = new SemaphoreSlim(1, 1);
private int _readerCount;
public async Task AcquireWriterLock(CancellationToken token = default)
{
await _writeSemaphore.WaitAsync(token).ConfigureAwait(false);
await SafeAcquireReadSemaphore(token).ConfigureAwait(false);
}
public void ReleaseWriterLock()
{
_readSemaphore.Release();
_writeSemaphore.Release();
}
public async Task AcquireReaderLock(CancellationToken token = default)
{
await _writeSemaphore.WaitAsync(token).ConfigureAwait(false);
if (Interlocked.Increment(ref _readerCount) == 1)
{
try
{
await SafeAcquireReadSemaphore(token).ConfigureAwait(false);
}
catch
{
Interlocked.Decrement(ref _readerCount);
throw;
}
}
_writeSemaphore.Release();
}
public void ReleaseReaderLock()
{
if (Interlocked.Decrement(ref _readerCount) == 0)
{
_readSemaphore.Release();
}
}
private async Task SafeAcquireReadSemaphore(CancellationToken token)
{
try
{
await _readSemaphore.WaitAsync(token).ConfigureAwait(false);
}
catch
{
_writeSemaphore.Release();
throw;
}
}
public void Dispose()
{
_writeSemaphore.Dispose();
_readSemaphore.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)