2012-05-23 1 views
10

Ich versuche, eine generische thread-sichere Cache-Methode zu implementieren, und ich frage mich, wie ich die Sperre darin implementieren sollte.Wie kann ich nach Cache-Schlüssel sperren?

Es sollte wie folgt aussehen:

//private static readonly lockObject = new Object(); 

public T GetCache<T>(string key, Func<T> valueFactory...) 
{ 

    // try to pull from cache here 

    lock (lockObject) // I don't want to use static object lock here because then every time a lock is performed, all cached objects in my site have to wait, regarding of the cache key. 
    { 
    // cache was empty before we got the lock, check again inside the lock 

    // cache is still empty, so retreive the value here 

    // store the value in the cache here 
    } 

    // return the cached value here 

} 
+0

Was möchten Sie erreichen? Es scheint mir, dass Sie zwischen zwei Arten von Sperren verwirren: Sperren, um sicherzustellen, dass nur ein Thread das zwischengespeicherte Objekt bearbeiten kann und eine Sperre, um sicherzustellen, dass das Hinzufügen/Entfernen aus dem Cache threadsicher ist. Das Hinzufügen einer Sperre pro Objekt wird Ihnen helfen, sicherzustellen, dass nur ein Thread es ändern kann, aber es hat nichts damit zu tun, es aus dem Cache hinzuzufügen/zu entfernen (welche Implementierung Sie nicht zur Verfügung gestellt haben) Wörterbuch). Ich denke nicht, dass Sie die Cache - Sammel - Level - Sperre sowieso ablehnen können. – YavgenyP

+0

Ich benutze Asp.net Cache ... also muss ich nicht mein eigenes Wörterbuch implementieren ... Und ich spreche über Locking für das Hinzufügen ... Sagen wir, 1000 Leute haben die Seite zur gleichen Zeit angefordert, ich will nur eine von ihnen zu cachen, um Objekt, und alle anderen, um es zu verwenden ... – Amir

+0

Sie müssen immer noch die zugrunde liegende Sammlung und nicht das Objekt sperren. Was Sie tun wollen, wenn ich es richtig verstehe, ist zu prüfen, ob das Objekt in dieser Sammlung nicht vorhanden ist, und erst dann hinzuzufügen. Wenn Sie .net 4 verwenden, können Sie eine ihrer neuen Blockierungssammlungen verwenden, z. B. ConcurrentDictionary, um sicherzustellen, dass jeder Schlüssel nur einmal hinzugefügt wird. – YavgenyP

Antwort

1

Dies ist der Fall, dass Sie die Verwendung mutex

public T GetCache<T>(string key, Func<T> valueFactory...) 
{ 
    // note here that I use the key as the name of the mutex 
    // also here you need to check that the key have no invalid charater 
    // to used as mutex name. 
    var mut = new Mutex(true, key); 

    try 
    { 
     // Wait until it is safe to enter. 
     mut.WaitOne(); 

     // here you create your cache 
    } 
    finally 
    { 
     // Release the Mutex. 
     mut.ReleaseMutex(); 
    } 
} 
+0

Auch wenn er ein Lock- oder Sync-Objekt jeglicher Art benötigt - warum sollte er Mutex benutzen und nicht überwachen? "Die Mutex-Klasse verwendet mehr Systemressourcen als die Monitor-Klasse" (aus [msdn]) – YavgenyP

+1

@YavgenyP gibt es zwei Hauptgründe: 1) zum Sperren aller Threads, die diesen Cache verwenden/erstellen, und nur eines Threads. 2) Geben Sie einen Namen und sperren Sie nur den gleichen Teil des Cache und nicht jeden Cache, der angefordert wird. - Nun, ich weiß nicht, ob er synchronisieren muss oder nicht, ich wiederhole es ihm heiß, es zu tun und streite nicht, wenn er es braucht oder nicht. – Aristos

0

.NET ConcurrentDictionary<TKey, TItem> diese intern implementiert durch eine separate Sperre für jeden zu schaffen Schlüsselhash. Dies hat den Vorteil, dass nur der eine relevante Hash gesperrt wird, auch wenn Artikel hinzugefügt und entfernt werden.

1

Ich habe gerade gefunden LazyCache lib. Ich habe es noch nicht in der Produktion versucht.

IAppCache cache = new CachingService(); 
ComplexObject cachedResults = cache.GetOrAdd("uniqueKey", 
    () => methodThatTakesTimeOrResources()); 
+0

Danke für den Link - ich benutze es in der Produktion und es funktioniert gut, aber ich würde sagen, dass - ich habe es geschrieben. – alastairtree