C# ReaderWriterLockSlim和async\Wait
我对ReaderWriterLockSlim有一些问题。我不明白它是怎么变魔术的 我的代码:C# ReaderWriterLockSlim和async\Wait,c#,asynchronous,locking,C#,Asynchronous,Locking,我对ReaderWriterLockSlim有一些问题。我不明白它是怎么变魔术的 我的代码: private async Task LoadIndex() { if (!File.Exists(FileName + ".index.txt")) { return; } _indexLock.EnterWriteLock();// <1> _index.Clear();
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>
}
private async Task LoadIndex()
{
如果(!File.Exists(FileName+“.index.txt”))
{
返回;
}
_indexLock.EnterWriteLock();//
_index.Clear();
使用(TextReader index=File.OpenText(FileName+“.index.txt”))
{
字符串s;
while(null!=(s=await index.readlinesync())
{
var ss=s.Split(“:”);
_index.Add(ss[0],Convert.ToInt64(ss[1]);
}
}
_ExitWriteLock();
}
在调试器中输入write lock at时,我可以看到\u indexLock.iswritelockhold
是true
,但当执行步骤到时,我看到\u indexLock.iswritelockhold
是false
和
\u indexLock.ExitWriteLock
抛出异常SynchronizationLockException
,并显示消息“正在释放写锁而不保持”。我做错了什么?ReaderWriterLockSlim
是一种线程仿射锁类型,因此它通常不能与异步
和等待
一起使用
您应该将
SemaphoreSlim
与WaitAsync
一起使用,或者(如果您真的需要读写器锁),使用my or。您可以使用可靠且轻量级的SemaphoreSlim
安全地模拟读写器锁定机制,并保持异步的优点。创建SemaphoreSlim
,为其提供与锁定资源以同时读取的例程数量相等的可用锁数量。每个人都会像往常一样申请一把锁。对于您的写入例程,确保它在执行操作之前请求所有可用的锁。
这样,您的写入例程将始终单独运行,而您的读取例程可能只在它们之间共享资源。
例如,假设您有2个读取例程和1个写入例程
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)
}
}
显然,只有事先知道可以同时运行多少个读取例程,这种方法才有效。不可否认,它们太多会使这段代码变得非常丑陋。不久前,我基于两个信号量lim为我的项目类AsyncReaderWriterLock实现了。希望能有所帮助。它采用相同的逻辑(多个读卡器和单个写卡器)实现,同时支持异步/等待模式。当然,它不支持递归,也没有防止错误使用的保护:
var rwLock = new AsyncReaderWriterLock();
await rwLock.AcquireReaderLock();
try
{
// ... reading ...
}
finally
{
rwLock.ReleaseReaderLock();
}
await rwLock.AcquireWriterLock();
try
{
// ... writing ...
}
finally
{
rwLock.ReleaseWriterLock();
}
正如Stephen Cleary所说,ReaderWriterLockSlim是一种线程仿射锁类型,因此它通常不能与async和await一起使用
您必须建立一种机制来避免读写器同时访问共享数据。这个算法应该遵循一些规则
请求readerlock时:
- 是否有一个writerlock处于活动状态
- 有什么东西已经排队了吗?(我认为按照请求的顺序执行它很好)
请求writerlock时:
- 是否有一个writerlock处于活动状态?(因为writerlock不应该并行执行)
- 是否有任何读卡器锁处于活动状态
- 有什么东西已经排队了吗
如果这些creteria中的任何一个回答为“是”,则执行应排队,稍后再执行。您可以使用继续等待
完成任何读写器后,应评估队列并在可能的情况下继续执行项
例如(nuget):
来源:
ReaderWriterLockSlim已管理线程关联;也就是说,每个线程
对象必须进行自己的方法调用才能进入和退出锁定模式。不
线程可以更改另一个线程的模式
所以这里是预期行为。Async/await不保证在同一个线程中继续,所以当您在一个线程中输入写锁并尝试在另一个线程中退出时,可以捕获异常
最好使用其他答案中的其他锁定机制,如信号量lim
如何初始化\u indexLock
?另一个线程是否可以在另一个线程位于LoadIndex()
的同时对其进行初始化?可能另一个有权访问_indexLock的线程正在重新初始化它。。。。它肯定不能被另一个线程释放,但可能会一起重新初始化为一个新实例……它不需要一个线程来获取要覆盖的_indexLock。_indexLock在类istance创建和声明时被初始化,如下所示:private readonly readerwriterlocksim _indexLock=new readerwriterlocksim()。没有一个线程可以修改它。这不提供读/写语义。读写器锁的概念是,可以有任意数量的并发读写器,但当写操作正在进行时,就不能有任何读写器或其他写入器。这既不必要地限制了读卡器,也不能在一个操作正在写入时正确地限制读卡器或其他写入器。当您有活动读卡器时,我会阻止任何写入器,当您没有任何活动写入器时,它仍然允许并发读卡器。这基本上就是读写器锁应该做的,除非你不知道你会有多少个读写器,正如我明确指出的(如果你真的想一想,即使这样也可以做到,但我不会破坏你的惊喜)。对于您的标准来说,它也可能不那么优雅,但它是一个有效的解决方案,我自己在经过良好测试、压力很大的高需求服务中使用了它。我只想简单一点,但每个都有自己的特点……这对于一个不依赖外部依赖的快速解决方案来说并不坏。如果需要的话,可能应该包装到它自己的类中以供重用(为每次读取调用多个.WaitAsync()
,并记住您容纳了多少个读卡器)
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)
{
await SafeAcquireReadSemaphore(token).ConfigureAwait(false);
}
_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();
}
}