2013-10-29 55 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()'),並記住每次使用時佔用的讀者數量,並在必要時在每次使用時更改它,和容易發生錯誤)。 –