Implementazione Silverlight ReaderWriterLock Buono / cattivo?

Ho adottato un’implementazione di un ReaderWriterLock semplice (senza aggiornamenti o timeout) per Silverlight, mi chiedevo se chiunque avesse le competenze giuste può convalidare se è buono o cattivo in base alla progettazione. A me sembra tutto ok, funziona come pubblicizzato, ma ho un’esperienza limitata con il codice multi-threading in quanto tale.

public sealed class ReaderWriterLock { private readonly object syncRoot = new object(); // Internal lock. private int i = 0; // 0 or greater means readers can pass; -1 is active writer. private int readWaiters = 0; // Readers waiting for writer to exit. private int writeWaiters = 0; // Writers waiting for writer lock. private ConditionVariable conditionVar; // Condition variable. public ReaderWriterLock() { conditionVar = new ConditionVariable(syncRoot); } ///  /// Gets a value indicating if a reader lock is held. ///  public bool IsReaderLockHeld { get { lock ( syncRoot ) { if ( i > 0 ) return true; return false; } } } ///  /// Gets a value indicating if the writer lock is held. ///  public bool IsWriterLockHeld { get { lock ( syncRoot ) { if ( i < 0 ) return true; return false; } } } ///  /// Aquires the writer lock. ///  public void AcquireWriterLock() { lock ( syncRoot ) { writeWaiters++; while ( i != 0 ) conditionVar.Wait(); // Wait until existing writer frees the lock. writeWaiters--; i = -1; // Thread has writer lock. } } ///  /// Aquires a reader lock. ///  public void AcquireReaderLock() { lock ( syncRoot ) { readWaiters++; // Defer to a writer (one time only) if one is waiting to prevent writer starvation. if ( writeWaiters > 0 ) { conditionVar.Pulse(); Monitor.Wait(syncRoot); } while ( i < 0 ) Monitor.Wait(syncRoot); readWaiters--; i++; } } ///  /// Releases the writer lock. ///  public void ReleaseWriterLock() { bool doPulse = false; lock ( syncRoot ) { i = 0; // Decide if we pulse a writer or readers. if ( readWaiters > 0 ) { Monitor.PulseAll(syncRoot); // If multiple readers waiting, pulse them all. } else { doPulse = true; } } if ( doPulse ) conditionVar.Pulse(); // Pulse one writer if one waiting. } ///  /// Releases a reader lock. ///  public void ReleaseReaderLock() { bool doPulse = false; lock ( syncRoot ) { i--; if ( i == 0 ) doPulse = true; } if ( doPulse ) conditionVar.Pulse(); // Pulse one writer if one waiting. } ///  /// Condition Variable (CV) class. ///  public class ConditionVariable { private readonly object syncLock = new object(); // Internal lock. private readonly object m; // The lock associated with this CV. public ConditionVariable(object m) { lock (syncLock) { this.m = m; } } public void Wait() { bool enter = false; try { lock (syncLock) { Monitor.Exit(m); enter = true; Monitor.Wait(syncLock); } } finally { if (enter) Monitor.Enter(m); } } public void Pulse() { lock (syncLock) { Monitor.Pulse(syncLock); } } public void PulseAll() { lock (syncLock) { Monitor.PulseAll(syncLock); } } } } 

Se è buono, potrebbe essere d’aiuto anche ad altri, dato che al momento Silverlight non ha un tipo di blocco lettore-scrittore. Grazie.

Approfondisco la spiegazione del ReaderWriterLock di Vance Morrison (diventato ReaderWriterLockSlim in .NET 3.5) sul mio blog (fino al livello x86). Questo potrebbe essere utile nel tuo progetto, specialmente capire come funzionano davvero le cose.

Entrambi i metodi IsReadorLockHeld e IsWriterLockHeld sono imperfetti a livello concettuale. Mentre è ansible determinare che in un dato momento un particolare blocco è o non è tenuto, non c’è assolutamente nulla che tu possa tranquillamente fare senza queste informazioni a meno che tu continui a tenere il lucchetto (non nel tuo codice).

Questi metodi dovrebbero essere denominati più accuratamente WasReadLockHeldInThePast e WasWriterLockHeldInThePast. Una volta rinominati i metodi per una rappresentazione più accurata di ciò che fanno, diventa più chiaro che non sono molto utili.

Questa class mi sembra più semplice e fornisce la stessa funzionalità. Potrebbe essere leggermente meno performante, dal momento che è sempre PulsesAll (), ma la logica è molto più semplice da capire, e dubito che il successo della performance sia così grande.

 public sealed class ReaderWriterLock() { private readonly object internalLock = new object(); private int activeReaders = 0; private bool activeWriter = false; public void AcquireReaderLock() { lock (internalLock) { while (activeWriter) Monitor.Wait(internalLock); ++activeReaders; } } public void ReleaseReaderLock() { lock (internalLock) { // if activeReaders < = 0 do some error handling --activeReaders; Monitor.PulseAll(internalLock); } } public void AcquireWriterLock() { lock (internalLock) { // first wait for any writers to clear // This assumes writers have a higher priority than readers // as it will force the readers to wait until all writers are done. // you can change the conditionals in here to change that behavior. while (activeWriter) Monitor.Wait(internalLock); // There are no more writers, set this to true to block further readers from acquiring the lock activeWriter = true; // Now wait till all readers have completed. while (activeReaders > 0) Monitor.Wait(internalLock); // The writer now has the lock } } public void ReleaseWriterLock() { lock (internalLock) { // if activeWriter != true handle the error activeWriter = false; Monitor.PulseAll(internalLock); } } }