2010-12-17 2 views
8

ich Antritt mit einigem .NET 3.5-Code zu arbeiten und fand die folgende Erweiterungsmethode für einen Cache verwendet wird ::Für .NET 3.5 wie würden Sie einen thread-sicheren Cache erstellen oder hinzufügen?

public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> @this, TKey key,Func<TKey,TValue> factory,bool useLocking) 
{ 

    TValue value; 
    if([email protected](key,out value)) 
    { 
     if (useLocking) 
     { 
      lock ((@this as ICollection).SyncRoot) 
      { 
       if ([email protected](key, out value)) 
       { 
        @this[key] = value = factory(key); 
       } 
      } 
     } 
     else 
     { 
      @this[key] = value = factory(key); 
     } 
    } 
    return value; 
} 

Der Cache in Frage String-Schlüssel verschlüsselt ist, und mit useLocking = true. Es wird immer auf diese Weise zugegriffen (es gibt keine streunenden TryGetValue). Es gibt auch kein Problem bei der Verwendung der Eigenschaft SyncRoot, da das Wörterbuch privat ist und nirgendwo anders verwendet wird. Die doppelte Sperre ist gefährlich, da ein Wörterbuch das Lesen während des Schreibens nicht unterstützt. Während technisch noch kein Problem gemeldet wurde, da das Produkt nicht ausgeliefert wurde, glaube ich, dass dieser Ansatz zu Rennbedingungen führen wird.

  1. Schalten Sie den Dictionary<,> einen Hashtable. Wir werden die Typensicherheit verlieren, aber wir werden in der Lage sein, das von uns angestrebte Nebenläufigkeitsmodell zu unterstützen (1 Autor, mehrere Leser).

  2. Entfernen Sie den äußeren TryGetValue. Auf diese Weise muss jeder Lesevorgang eine Sperre erhalten. Dies ist möglicherweise schlecht für die Leistung, aber eine unangekündigte Sperre sollte ziemlich billig sein.

Beide sind ziemlich beschissen. Hat jemand einen besseren Vorschlag? Wenn dies .NET 4-Code wäre, würde ich es nur auf ConcurrentDictionary umstellen, aber ich habe diese Option nicht.

+5

Yargh! Völlig unzusammenhängend ... aber schreibe keinen Code mit @this. Du bist in einer statischen Methode; Versuchen Sie nicht, es wie eine Instanzmethode aussehen zu lassen. –

+0

das ist nicht mein Code! Wenn es mein Code wäre, würde ich die Argumentvalidierung hinzufügen. –

Antwort

0

Entfernen Sie den TryGetValue. Ich wette, Sie werden das Nebenläufigkeitsproblem nicht sehen; CLR-Monitore sind ziemlich schnell und "unfair", so dass Konvoi- oder Prioritätsinversionsprobleme nicht auftreten.

Wenn Sie ein Nebenläufigkeitsproblem sehen, dann ist die nächste beste Sache ein ReaderWriterLockSlim. Leider werden Sie dafür eine neue Klasse erstellen müssen, anstatt eine Erweiterungsmethode zu verwenden, da Sie einen Speicherort für die Sperre benötigen.

Wenn Sie diesen Weg gehen, achten Sie darauf, von der Aktualisierung von Lese- auf Schreibsperre zu verzichten.

0

Ich denke, dass Sie die vollständige Aussage sperren müssen (wie Sie sagten).

Es gibt jedoch eine saubere Lösung im Web für ein zukünftiges .NET 4-Upgrade: Haben Sie eine benutzerdefinierte Klasse anstelle eines Wörterbuchs, behalten Sie das Wörterbuch als private Membervariable und alle Zugriffe auf das Wörterbuch in einem Thread -Safe-Lock-Anweisung. Detaillierte Beschreibung finden Sie hier: http://www.grumpydev.com/2010/02/25/thread-safe-dictionarytkeytvalue/

2

Ja, Ihr Verdacht ist richtig; Es gibt eine Race Condition und alle Methoden, auch TryGetValue müssen in der von Ihnen vorgestellten Implementierung im Schloss sein.

Was die Leistung betrifft, können Sie sich auf den Tag freuen, an dem Sie ein Upgrade auf .NET4 durchführen können, das standardmäßig ausnahmslos ConcurrentDictionary enthält.

Diese Ergebnisse legen nahe, mir, dass die beste Umsetzung für .NET3.5 ist Dictionary Plus ReadWriteLockSlim und für eine gute Maßnahme, hier ist eine vollständige Implementierung:

Update:

Ich las die Tabellen falsch, und es sieht aus wie Dictionary + lock wenig schneller als der einzige andere ernsthafte Anwärter Dictionary + ReadWriteLockSlim.

+1

Lustig, diese Ergebnisse deuten auf * mich * hin, dass ein 'Dictionary' mit Mutex-Locking die beste Implementierung ist. Es ist schneller als die rw-lock-Implementierung. – Gabe

+0

Hoppla, ich habe zu schnell gescannt und diese Tabelle als Operationen pro Zeiteinheit gelesen, anstatt pro Einheitsoperationen. –

+1

Der Artikel mit der vollständigen Thread Safe Dictionary-Implementierung ist weg, aber nach ziemlich viel graben denke ich, dass ich die [Implementierung selbst] gefunden habe (http://www.codekeep.net/snippets/b2afc386-13cc-4297- a327-dbf52dd7498a.aspx) (nur Code, kein Kommentar/Erklärung). – Dusty

1

Meine Empfehlung wäre, Reactive Extensions for .NET (Rx), die Backports der Sammlungen im System.Collections.Concurrent Namespace (das enthält ConcurrentDictionary<TKey, TValue>) für .NET 3.5 enthält.

+2

Die sind nicht mehr da von 19. Oktober 2012. – rickythefox

Verwandte Themen