2013-10-29 24 views
11

ich mit ReaderWriterLockSlim einige Probleme haben. Ich kann nicht verstehen, wie es funktioniert.ReaderWriterLockSlim und async erwarten

Mein Code:

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

Wenn ich Schreibsperre auf < 1>, in Debugger eingeben kann ich sehen, dass _indexLock.IsWriteLockHeldtrue ist, aber wenn die Ausführung Schritte < 2> Ich sehe _indexLock.IsWriteLockHeldfalse und _indexLock.ExitWriteLock löst eine Ausnahme SynchronizationLockException mit der Meldung "Die Schreibsperre wird freigegeben, ohne gehalten zu werden". Was mache ich falsch?

+0

Wie wird '_indexLock' initialisiert? Könnte ein anderer Thread es gleichzeitig mit einem anderen Thread in 'LoadIndex()' initialisieren? –

+0

vielleicht ein anderer Thread, der Zugriff auf _indexLock hat, reinitialisiert es .... es konnte nicht von einem anderen Thread sicher freigegeben werden, aber möglicherweise zu einer neuen Instanz alle zusammen reinitialisiert ... –

+0

Es erfordert keinen Thread zu bekommen _indexLock wird überschrieben. –

Antwort

24

ReaderWriterLockSlim ist ein Thread-affine Lock-Typ, so kann es normalerweise nicht mit async und await verwendet werden.

Sie entweder SemaphoreSlim mit WaitAsync verwenden sollten, oder (wenn Sie wirklich einen Leser/Schreiber-Sperre müssen), verwenden Sie meine AsyncReaderWriterLock from AsyncEx oder Stephen Toub's AsyncReaderWriterLock.

+0

Wäre es nicht auch richtig zu sagen, dass es von der Plattform abhängt? Der von OP gepostete Code erwähnt nicht, ob es WPF oder eine Konsolen-App ist. In einer Konsolen-App funktioniert es nicht, weil dort kein 'SynchronizationContext' vorhanden ist. Der Code funktioniert in einer WPF-App einwandfrei. http://stackoverflow.com/questions/15882710/is-it-safe-to-use-readerwriterlocks-lim-in-a-async-method?noredirect=1&lq=1 –

+0

@DonBox: Nein; Wie ich in meiner Antwort auf diese Frage bemerkt habe, erlauben Sie, auch wenn Sie mit demselben Thread fortfahren, immer noch willkürlichen Code zu laufen, während Sie diese Sperre "halten". –

+0

Oh Mann, und ich dachte, ich habe es richtig gemacht. Können Sie erklären, was Sie mit "willkürlichem Code" meinen? Sie meinen, dass der Code nicht richtig funktioniert, nicht einmal in WPF? Was ist falsch daran, das Schloss dort zu halten? In der Antwort, was meintest du mit "normalerweise" "es kann normalerweise nicht verwendet werden"? Vielen Dank! –

0

Mit dem zuverlässigen und leichten SemaphoreSlim können Sie einen Lese-/Schreibgerät-Verriegelungsmechanismus sicher emulieren und die Vorteile von async/await beibehalten. Erstellen Sie SemaphoreSlim und geben Sie die Anzahl der verfügbaren Sperren an, die der Anzahl der Routinen entspricht, die Ihre Ressource gleichzeitig lesen. Jeder wird wie üblich eine Sperre anfordern. Stellen Sie für Ihre Schreibroutine sicher, dass sie alle verfügbaren Sperren anfordert, bevor Sie ihre Aufgabe ausführen.

Auf diese Weise wird Ihr Schreiben Routine immer alleine laufen, während die Leseroutinen die Ressource zwischen sich nur gemeinsam nutzen könnten.

Angenommen, Sie haben 2 Lese- und 1 Schreibroutine.

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

Offensichtlich funktioniert diese Methode nur, wenn Sie vorher wissen, wie viele Lesefunktionen Sie gleichzeitig ausführen können. Zugegeben, zu viel von ihnen würden diesen Code sehr hässlich machen.

+1

Dies bietet keine Lese-/Schreib-Semantik. Die Idee einer Leser/Schreiber-Sperre ist, dass es beliebig viele gleichzeitige Leser geben kann, aber wenn eine Schreiboperation stattfindet, kann es keine Leser oder andere Schreiber geben. Dies begrenzt sowohl die Leser unnötig als auch die Lesegeräte oder andere Schreiber nicht richtig, wenn eine Operation schreibt. – Servy

+0

Ich blockiere alle Schreiber, wenn Sie aktive Leser haben, und es erlaubt immer noch gleichzeitige Leser, wenn Sie keine aktiven Schreiber haben. Das ist im Wesentlichen, was ein Leser/Schreiber-Lock tun sollte, außer wenn Sie nicht wissen, wie viele Leser Sie haben würden, wie ich ** deutlich ** darauf hingewiesen habe (sogar das könnte herumkommen, wenn Sie wirklich darüber nachdenken, aber Ich würde die Überraschung für dich nicht verderben). Es ist vielleicht auch nicht so elegant für Ihre Standards, aber es ist ** eine gültige Lösung, und ich selbst habe das in gut getesteten, stark beanspruchten High-Demand-Diensten verwendet. Es geht mir einfach darum, einfach zu gehen, aber jeder für sich ... – mycelo

+0

Das ist nicht schlecht für eine schnelle Lösung, die nicht auf externe Abhängigkeiten angewiesen ist. Sollte wahrscheinlich in eine eigene Klasse zur Wiederverwendung eingeschlossen werden (wenn mehrere ".WaitAsync()" für jeden Lesevorgang aufgerufen werden, plus die Erinnerung, wie viele Leser Sie bei jeder Verwendung abgerechnet haben und diese bei jeder Verwendung ändern, wäre dies hässlich und fehleranfällig). –