2013-10-29 32 views
11

我有一些问题ReaderWriterLockSlim。我无法理解它是如何运作的。ReaderWriterLockSlim和异步 await

我的代码:

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> 
    } 

当我在< 1>进入写锁定,在调试器我可以看到_indexLock.IsWriteLockHeldtrue,但是当执行步骤< 2>我看到_indexLock.IsWriteLockHeldfalse_indexLock.ExitWriteLock抛出一个异常SynchronizationLockException带有消息“写入锁正在释放而未被占用”。我做错了什么?

+0

'_indexLock'如何初始化?另一个线程是否可以在不同的线程同时在'LoadIndex()'中初​​始化它? –

+0

也许另一个有权访问_indexLock的线程正在重新初始化它......它肯定不能被另一个线程释放,但可能重新初始化为一个新实例...... –

+0

它不需要线程来获取_indexLock被覆盖。 –

回答

24

ReaderWriterLockSlim是一个线程仿射锁定类型,所以它通常不能与asyncawait一起使用。

您应该使用SemaphoreSlimWaitAsync,或者(如果你真的需要读取/写入器锁),用我的AsyncReaderWriterLock from AsyncExStephen Toub's AsyncReaderWriterLock

+0

说它取决于平台也不正确吗?由OP发布的代码没有提及它是WPF还是控制台应用程序。在控制台应用程序中,它不起作用,因为那里没有'SynchronizationContext'。该代码在WPF应用程序中可以正常工作。 http://stackoverflow.com/questions/15882710/is-it-safe-to-use-readerwriterlockslim-in-an-async-method?noredirect=1&lq=1 –

+0

@收件箱:不;正如我在回答这个问题时指出的那样,即使你在同一个线程上恢复,你仍然允许任意代码在你“持有”该锁的时候运行。 –

+0

噢,我认为我说得对。你能解释一下“任意代码”是什么意思吗?你的意思是,即使在WPF中,代码也无法正常工作?那里拿着锁有什么问题?在答案中,你的意思是“通常”,“通常不能使用”?谢谢! –

0

您可以安全地使用可靠和轻量级的SemaphoreSlim模拟读写器锁定机制,并保持async/await的优势。创建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) 
    } 
} 

显然这种方法,如果你知道你可以事先有多少阅读程序在同一时间仅运行工作。无可否认,他们太多会使这个代码丑陋。

+1

这不提供读/写语义。读者/写者锁的想法是可以有任意数量的并发读者,但是当发生写操作时,不会有任何读者或其他作者。这既会不必要地限制读者,也不会在一个操作正在写入时正确限制读者或其他作者。 – Servy

+0

当你拥有活跃的阅读器时,我会阻止任何作者,并且当你没有任何活跃的阅读器时,它仍然允许并发阅读器。这基本上是一个读者/写作者锁定应该做的事情,除非你不知道有多少读者,因为我清楚地指出了这一点(甚至如果你真的想到它,也可能会有这种情况发生)我不会破坏你的惊喜)。它对你的标准来说可能并不是那么优雅,但它是一个有效的解决方案,我自己也在经过充分测试的高压力高需求服务中使用。我只想简单一点,但每个都是自己的... – mycelo

+0

这对于不依赖外部依赖的快速解决方案来说并不坏。如果需要的话,可能需要将其封装到自己的类中以供重用(为每次读取调用多个'.WaitAsync()'),并记住每次使用时占用的读者数量,并在必要时在每次使用时更改它,和容易发生错误)。 –