Frage

Kürzlich habe ich einige meiner C# -Codes neu gestaltet und habe einige doppelte Überprüfungsverriegelungspraktiken gefunden. Ich wusste nicht, dass es damals eine schlechte Praxis war und ich möchte es wirklich loswerden.

Das Problem ist, dass ich eine Klasse habe, auf die faul initialisiert und häufig von vielen Threads zugegriffen werden sollte. Ich möchte die Initialisierung auch nicht in einen statischen Initialisierer verschieben, da ich vorhabe, einen schwachen Verweis zu verwenden, um zu verhindern, dass das initialisierte Objekt zu lange im Speicher bleibt. Bei Bedarf möchte ich jedoch das Objekt "wiederbeleben", um sicherzustellen, dass dies auf fadensichere Weise geschieht.

Ich habe mich gefragt, ob die Verwendung eines ReaderwriterLlockslims in C# verwendet und vor dem ersten Scheck einen UpgradeAbler -Leadlock eingeben würde, und wenn dies dann eine Schreibschloss für die Initialisierung eingeben würde, wäre eine akzeptable Lösung. Hier ist, was ich im Sinn habe:

public class LazyInitialized
{
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

    private volatile WeakReference _valueReference = new WeakReference(null);
    public MyType Value
    {
        get
        {
            MyType value = _valueReference.Target as MyType;
            _lock.EnterUpgradeableReadLock();
            try
            {
                if (!_valueReference.IsAlive) // needs initializing
                {
                    _lock.EnterWriteLock();
                    try
                    {
                        if (!_valueReference.IsAlive) // check again
                        {
                            // prevent reading the old weak reference
                            Thread.MemoryBarrier(); 
                            _valueReference = new WeakReference(value = InitializeMyType());
                        }
                    }
                    finally
                    {
                        _lock.ExitWriteLock();
                    }
                }
            }
            finally
            {
                _lock.ExitUpgradeableReadLock();
            }
            return value;
        }       
    }

    private MyType InitializeMyType()
    {
        // code not shown    
    }
}

Mein Punkt ist, dass kein anderer Thread versuchen sollte, das Element erneut zu initialisieren, während viele Threads gleichzeitig lesen sollten, sobald der Wert initialisiert ist. Das aufgerüstbare Lesesperre sollte alle Leser blockieren, wenn die Schreibschloss erfasst wird. Daher ist das Verhalten, während das Objekt initialisiert wird, einer Sperranweisung ähnlich, bei der das aufgerüstbare Lesesperre beginnt. Nach der Initialisierung ermöglicht die aufgerüstbare Lesesperrung mehrere Threads, weshalb der Leistungstreffer von jedem Thread nicht vorhanden ist.

Ich habe auch einen Artikel gelesen hier Wenn Sie sagen, dass volatile Speicherbarrieren automatisch vor dem Lesen und nach dem Schreiben automatisch eingefügt werden können, gehe ich davon aus, dass nur eine manuell definierte Barriere zwischen dem Lesen und dem Schreiben ausreicht, um sicherzustellen, dass das _Valuereference -Objekt korrekt gelesen wird. Ich werde Ihre Ratschläge und Kritik an diesem Ansatz gerne zu schätzen wissen.

War es hilfreich?

Lösung

Um den Punkt zu betonen, den @Mannimarco macht: Wenn dies der einzige Zugriffspunkt für den Wert ist und dies so aussieht, ist Ihr gesamtes ReaderwriterLlockslim -Setup nicht besser als ein einfacher Monitor. Es ist jedoch viel komplizierter.

Ich glaube also, dass der folgende Code in Funktion und Effizienz gleichwertig ist:

private WeakReference _valueReference = new WeakReference(null);
private object _locker = new object();

public MyType Value
{    
  get
  {    
    lock(_locker)  // also provides the barriers
    {
        value = _valueReference.Target;

        if (!_valueReference.IsAlive)
        {
            _valueReference = new WeakReference(value = InitializeMyType());
        }
        return value; 
    }
  }    
}

Andere Tipps

Eine Warnung: Nur ein einzelner Thread kann jeweils den UpgradeAbleradlock -Modus eingeben. Kasse ReaderwriterLlockslim. Wenn sich Fäden also häufen, während der erste Thread in den Schreibmodus eingeht und das Objekt erstellt, haben Sie einen Flaschenhals, bis die Sicherung (hoffentlich) gelöst ist. Ich würde ernsthaft empfehlen, einen statischen Initialisierer zu verwenden, es wird Ihnen das Leben erleichtern.

Bearbeiten: Je nachdem, wie oft das Objekt nachgebaut werden muss, würde ich tatsächlich empfehlen, die Monitor -Klasse und ihre Warte- und Pulsmethoden zu verwenden. Wenn der Wert nachgebaut werden muss, lassen Sie die Threads auf ein Objekt warten und ein anderes Objekt pulsieren, damit ein Arbeiter -Thread aufwachen und ein neues Objekt erstellen muss. Sobald das Objekt erstellt wurde, ermöglicht PulSAll allen Leser -Threads aufzuwachen und den neuen Wert zu greifen. (in der Theorie)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top