2013-06-11 1 views
11

Um den Zugriff auf meine Eigenschaften zu synchronisieren, verwende ich die ReaderWriterLockSlim Klasse. Ich verwende den folgenden Code, um thread-sicher auf meine Eigenschaften zuzugreifen.Microsofts Bemerkung zu ReaderWriterLockSlim.IsReadLockHeld/IsWriteLockHeld und seine Folgen

public class SomeClass 
{ 
    public readonly ReaderWriterLockSlim SyncObj = new ReaderWriterLockSlim(); 
    public string AProperty 
    { 
     get 
     { 
      if (SyncObj.IsReadLockHeld) 
       return ComplexGetterMethod(); 
      SyncObj.EnterReadLock(); 
      try 
      { 
       return ComplexGetterMethod(); 
      } 
      finally 
      { 
       SyncObj.ExitReadLock(); 
      } 
     } 
     set 
     { 
      if (SyncObj.IsWriteLockHeld) 
       ComplexSetterMethod(value); 
      else 
      { 
       SyncObj.EnterWriteLock(); 
       ComplexSetterMethod(value); 
       SyncObj.ExitWriteLock(); 
      } 
     } 
    } 

    // more properties here ... 

    private string ComplexGetterMethod() 
    { 
     // This method is not thread-safe and reads 
     // multiple values, calculates stuff, ect. 
    } 

    private void ComplexSetterMethod(string newValue)  
    { 
     // This method is not thread-safe and reads 
     // and writes multiple values. 
    } 
} 

// ===================================== 

public static SomeClass AClass = new SomeClass(); 
public void SomeMultiThreadFunction() 
{ 
    ... 
    // access with locking from within the setter 
    AClass.AProperty = "new value"; 
    ... 
    // locking from outside of the class to increase performance 
    AClass.SyncObj.EnterWriteLock(); 
    AClass.AProperty = "new value 2"; 
    AClass.AnotherProperty = "..."; 
    ... 
    AClass.SyncObj.ExitWriteLock(); 
    ... 
} 

Um unnötige Sperren zu vermeiden, wenn ich mehrere Objekte einmal veröffentlichte ich die ReaderWriterLockSlim -Object und sperren sie von außerhalb der Klasse jedes Mal, ich bin zu bekommen oder setzen eine Reihe von Eigenschaften und festlegen. Um dies zu erreichen, überprüfen meine Getter- und Setter-Methoden, ob das Schloss unter Verwendung der IsReadLockHeld-Eigenschaft und der IsWriteLockHeld-Eigenschaft von ReaderWriterLockSlim erfasst wurde. Das funktioniert gut und hat die Leistung meines Codes erhöht.

So weit so gut, aber wenn ich die Dokumentation über IsReadLockHeld und IsWriteLockHeld ich die Bemerkung Form Microsoft bemerkte erneut lesen: ist

Diese Eigenschaft zur bestimmungsgemäßen Verwendung in behauptet oder für andere Zwecke Debuggen . Verwenden Sie es nicht, um den Ablauf der Programmausführung zu steuern.

Meine Frage ist: Gibt es einen Grund, warum sollte ich nicht IsReadLockHeld/IsWriteLockHeld für diesen Zweck verwenden? Ist mit meinem Code etwas nicht in Ordnung? Alles funktioniert wie erwartet und viel schneller als rekursive Sperren (LockRecursionPolicy.SupportsRecursion).

Um dies zu verdeutlichen: Dies ist ein minimales Beispiel. Ich will nicht wissen, ob das Schloss selbst notwendig ist oder entfernt oder auf andere Weise erreicht werden kann. Ich möchte nur wissen, warum ich IsReadLockHeld/IsWriteLockHeld nicht verwenden sollte, um den Programmablauf zu steuern, wie in der Dokumentation angegeben.

+0

Der ReaderWriteLockSlim schützt nichts, .NET verspricht bereits, dass Objektreferenzaktualisierungen atomar sind. Der einzige milde Nebeneffekt ist, dass die Setter-Updates in anderen Threads sichtbar sind. Viel billiger, einfach MemoryBarrier() zu verwenden. Keine Thread-Sicherheitsgarantien wie auch immer, Sie sind sicher besser dran, SyncObj vollständig zu entfernen, da es nur ein falsches Gefühl der Sicherheit gibt. –

+0

@EricLippert: Die Dokumente für 'IsRead/WriteLockHeld' schlagen vor, dass sie' true' zurückgeben, wenn der * aktuelle Thread * die Sperre hält. Wenn "IsRead/WriteLockHeld" den Wert "true" zurückgibt, wie kann der Sperrstatus in diesem Thread geändert werden, ohne explizit auf "ExitRead/WriteLock" zu verweisen? – Iridium

+0

@Iridium: Ich ziehe meinen Kommentar zurück; Ich bin mir jetzt eigentlich nicht sicher, warum die Dokumentation diesen Vorbehalt macht. Das heißt: Best Practice ist zu tun, was die Dokumentation sagt. –

Antwort

12

Nach einigen weiteren Recherchen habe ich die gleiche Frage auf die German Support Forum of the Microsoft Developer Network gestellt und kam in die Diskussion mit dem sehr hilfreichen Moderator Marcel Roma. Er konnte den Programmierer des ReaderWriterLockSlimJoe Duffy, die diese Antwort schrieb an:

Ich fürchte, meine Antwort etwas verlassen kann zu wünschen übrig.

Die Eigenschaft funktioniert gut und wie dokumentiert. Die Anleitung ist wirklich nur , weil bedingte Erfassung und Freigabe von Sperren tendenziell fehlerhaft und fehleranfällig in der Praxis ist, vor allem mit Ausnahmen in der Mischung geworfen.

Es ist in der Regel eine gute Idee, Ihren Code so zu strukturieren, dass Sie entweder rekursive verwenden, oder Sie nicht (und natürlich ist letzteres immer einfacher zu begründen); mit Eigenschaften wie IsReadLockHeld landen Sie irgendwo in der Mitte.

Ich war einer der primären Designer von RWLS und ich muss zugeben, es hat viel zu viele Schnickschnack. Ich bedauere es nicht hinzufügen IsReadLockHeld - wie es zum Debuggen und Behauptungen nützlich sein kann - aber sobald wir es hinzugefügt, Pandora Box wurde geöffnet, und wir RWLS wurde sofort für diese Art der Nutzung geöffnet.

Ich bin nicht überrascht, dass die Leute es wie im StackOverflow Thread gezeigt verwenden möchten, und ich bin sicher, es gibt einige legitime Szenarien wo es besser funktioniert als die Alternativen. Ich rate nur irrend auf die Seite nicht zu verwenden.

Um die Dinge zusammenzufassen: Sie die IsReadLockHeld und die IsWriteLockHeld Eigenschaft verwenden, können eine Sperre bedingt zu erwerben, und alles wird gut funktionieren, aber es ist schlecht Programmierstil und man sollte es vermeiden. Es ist besser, bei rekursiven oder nicht-rekursiven Sperren zu bleiben. Um einen guten Codierungsstil beizubehalten, sollten IsReadLockHeld und IsWriteLockHeld nur für Debugging-Zwecke verwendet werden.

Ich möchte Marcel Roma und Joe Duffy nochmals für ihre wertvolle Hilfe danken.

-2

Dokumentation berät Sie das Richtige.

Betrachten Sie die folgende interleaved Ausführung.

Thread1.AcqrireReadLock(); 
Thread1.ComplexGetterMethod(); 
Thread2.ReadIsReaderLockHeldProperty(); 
Thread1.ReleaseReadLock(); 
Thread2.ComplexGetterMethod(); // performing read without lock. 

Die andere falsche Sache mit Ihrem Code, die ich sehe, ist

SyncObj.EnterReadLock(); 
try 
{ 
    return ComplexGetterMethod(); 
} 
finally 
{ 
    SyncObj.ExitReadLock(); 
} 

Dinge zu tun, nicht der richtige Weg ist. Dies ist ein Recht:

try 
{ 
    SyncObj.EnterReadLock(); 

    return ComplexGetterMethod(); 
} 
finally 
{ 
    if (SyncObj.IsReadLockHeld) 
     SyncObj.ExitReadLock(); 
} 

Und dies soll genaue Definition Ihrer Getter-Methode sein.

+0

Diese Ausführung wäre auf diese Weise nicht erfolgt, weil 'IsReadLockHeld' nur dann 'true' zurückgibt, wenn der ** aktuelle Thread ** die Sperre hält. – svick

+0

Mein Fehler. Du hast recht. – Egor