2009-03-04 4 views
3

J'ai une mise en œuvre adopted d'un simple ReaderWriterLock pour Silverlight (pas de mises à jour ou de dépassements de délai), je me demandais si quelqu'un avec la bonne expertise peut valider si c'est bon ou mauvais. Pour moi ça a l'air plutôt bien, ça marche comme annoncé, mais j'ai une expérience limitée du code multi-threading en tant que tel.Silverlight ReaderWriterLock Mise en œuvre Bonne/Mauvaise?

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

    /// <summary> 
    /// Gets a value indicating if a reader lock is held. 
    /// </summary> 
    public bool IsReaderLockHeld 
    { 
     get 
     { 
      lock (syncRoot) 
      { 
       if (i > 0) 
        return true; 
       return false; 
      } 
     } 
    } 

    /// <summary> 
    /// Gets a value indicating if the writer lock is held. 
    /// </summary> 
    public bool IsWriterLockHeld 
    { 
     get 
     { 
      lock (syncRoot) 
      { 
       if (i < 0) 
        return true; 
       return false; 
      } 
     } 
    } 

    /// <summary> 
    /// Aquires the writer lock. 
    /// </summary> 
    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. 
     } 
    } 

    /// <summary> 
    /// Aquires a reader lock. 
    /// </summary> 
    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++; 
     } 
    } 

    /// <summary> 
    /// Releases the writer lock. 
    /// </summary> 
    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. 
    } 

    /// <summary> 
    /// Releases a reader lock. 
    /// </summary> 
    public void ReleaseReaderLock() 
    { 
     bool doPulse = false; 
     lock (syncRoot) 
     { 
      i--; 
      if (i == 0) 
       doPulse = true; 
     } 
     if (doPulse) 
      conditionVar.Pulse();      // Pulse one writer if one waiting. 
    } 

    /// <summary> 
    /// Condition Variable (CV) class. 
    /// </summary> 
    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); 
      } 
     } 

    } 

} 

S'il est bon, il pourrait être utile pour les autres aussi, comme Silverlight ne dispose pas actuellement d'un type de verrou de lecture-écriture. Merci.

Répondre

4

je vais expliquer en profondeur sur Vance Morrison's ReaderWriterLock (qui est devenu ReaderWriterLockSlim dans .NET 3.5) on my blog (jusqu'au niveau x86). Cela pourrait être utile dans votre conception, en particulier la compréhension de la façon dont les choses fonctionnent vraiment.

+0

Hey merci pour votre écriture et me pointant vers la mise en œuvre de Vance Morrison. En fait, j'ai implémenté son implémentation sur Silverlight avec un minimum de changements, et il fonctionne aussi très vite. Je vais mettre quelques chiffres pour sauvegarder cela sur mon blog. À votre santé. –

1

Les deux méthodes IsReadorLockHeld et IsWriterLockHeld sont défectueuses au niveau conceptuel. Bien qu'il soit possible de déterminer qu'à un moment donné, un verrou particulier est ou n'est pas conservé, il n'y a absolument rien que vous puissiez faire en toute sécurité sans cette information sauf si vous continuez à maintenir le verrou (pas le cas dans votre code).

Ces méthodes s'appelleraient plus précisément WasReadLockHeldInThePast et WasWriterLockHeldInThePast. Une fois que vous avez renommé les méthodes pour une représentation plus précise de ce qu'elles font, il devient plus clair qu'elles ne sont pas très utiles.

+0

Vous avez raison, l'utilisation en dehors du consommateur actif serait une réponse «après-coup», ce qui n'est donc pas utile. Je vais supprimer les deux fonctions Is..Held, merci. –

0

Cette classe me semble plus simple et offre la même fonctionnalité. C'est peut-être un peu moins performant, puisque c'est toujours PulsesAll(), mais la logique est beaucoup plus simple à comprendre, et je doute que la performance soit aussi bonne.

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); 
     } 
    } 
} 
+0

semble doux, et les commentaires aident vraiment. Et pour Silverlight, je ne pense pas que la performance soit vraiment un problème dans ce cas, même si je vais essayer de vérifier. Merci, vous étiez très rapide avec ça. :) –

+0

Merci! J'aime mieux la réponse de Jeff Moser. :) – grieve