2010-12-17 9 views
8

Mam rozpoczęciem pracy z niektórych kodu .NET 3.5 i znaleźć następującą metodę rozszerzenia wykorzystywane do pamięci podręcznej ::Dla .NET 3.5 w jaki sposób można utworzyć wątek bezpieczny lub dodać pamięć podręczną?

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

Pamięć podręczna w pytaniu jest osadzone przez kluczami tekstowymi, iz useLocking = true. Jest zawsze dostępny za pomocą tej metody (nie ma bezpańskich TryGetValue). Nie ma również problemu z korzystaniem z właściwości SyncRoot, ponieważ słownik jest prywatny i nie ma innego miejsca, w którym jest używany. Podwójne blokowanie jest niebezpieczne, ponieważ słownik nie obsługuje odczytu podczas jego zapisywania. Chociaż technicznie nie zgłoszono jeszcze żadnego problemu, ponieważ produkt nie został wysłany, uważam, że takie podejście doprowadzi do warunków wyścigowych.

  1. Przejście do Dictionary<,> Do Hashtable. Stracimy bezpieczeństwo typu, ale będziemy w stanie obsłużyć model współbieżności, o który nam chodzi (1 autor, wielu czytelników).

  2. Usuń zewnętrzną wartość TryGetValue. W ten sposób każda lektura wymaga zdobycia zamka. Jest to potencjalnie złe z punktu widzenia wydajności, ale uzyskanie niekwestionowanej blokady powinno być dość tanie.

Obie są dość brzydkie. Czy ktoś ma lepszą sugestię? Gdyby to był kod .NET 4, po prostu przełączyłem go na ConcurrentDictionary, ale nie mam tej opcji.

+5

Yargh! Całkowicie niepowiązany ... ale nie pisz kodu używając @this. Jesteś w statycznej metodzie; nie próbuj sprawiać, by wyglądała jak metoda instancji. –

+0

to nie jest mój kod! Gdyby to był mój kod, dodałem walidację argumentów. –

Odpowiedz

0

Usuń parametr TryGetValue. Założę się, że nie zobaczysz problemu współbieżności; Monitory CLR są dość szybkie i "niesprawiedliwe", więc nie widać problemów z odwracaniem konwoju lub priorytetem.

Jeśli widzisz problem z współbieżnością, to najlepszą rzeczą jest ReaderWriterLockSlim. Niestety, zamiast korzystać z metody rozszerzenia, będziesz chciał stworzyć nową klasę, ponieważ będziesz potrzebować miejsca do przechowywania zamka.

Jeśli wybierzesz tę trasę, pamiętaj, aby nie zbliżać się do aktualizacji ze czytnika do zamków pisarskich.

0

Myślę, że należy zablokować kompletne oświadczenie (jak stwierdzono).

Istnieje jednak gotowe rozwiązanie w Internecie, które będzie przygotowane do przyszłej aktualizacji .NET 4: Miej niestandardową klasę zamiast słownika, zachowaj słownik jako prywatną zmienną i zawiń cały dostęp do słownika w wątku - instrukcja bezpiecznego blokowania. Szczegółowy opis można znaleźć tutaj: http://www.grumpydev.com/2010/02/25/thread-safe-dictionarytkeytvalue/

2

Tak, twoje podejrzenia są prawidłowe; istnieje warunek wyścigowy i wszystkie metody, nawet TryGetValue, muszą znajdować się wewnątrz blokady w przedstawionej wersji.

Jeśli chodzi o wydajność, można spodziewać się dnia, w którym można uaktualnić system do wersji .NET4, która zawiera wykrzyknik-szybką ConcurrentDictionary po wyjęciu z pudełka. Do tego czasu można przeglądać analiza Jamesa Michaela Hare'a zrobić tutaj:

Wyniki te sugerują mi, że najlepszym realizacja dla .NET3.5 jest Dictionary Plus ReadWriteLockSlim i na wszelki wypadek, oto pełna realizacja:

Aktualizacja:

czytam tabele źle i wygląda Dictionary + lock jest mało szybszy niż tylko inny poważny rywal Dictionary + ReadWriteLockSlim.

+1

Śmieszne, te wyniki sugerują, że * ja * najlepiej sprawdza się słowo 'Dictionary' z blokowaniem muteksa. Jest szybszy we wszystkich przypadkach, niż implementacja blokady rw-lock. – Gabe

+0

Ups, skanowałem zbyt szybko i odczytałem tabelę jako operacje na jednostkę czasu, zamiast czasu operacji na jednostkę. –

+1

Artykuł z pełną implementacją Nici Safe Dictionary zniknął, ale po dość długim kopaniu wydaje mi się, że znalazłem [samą implementację] (http://www.codekeep.net/snippets/b2afc386-13cc-4297- a327-dbf52dd7498a.aspx) (tylko kod, brak komentarzy/objaśnień). – Dusty

1

Moja rekomendacja to pobranie Reactive Extensions for .NET (Rx), która zawiera backporty kolekcji w przestrzeni nazw System.Collections.Concurrent (dotyczy to ConcurrentDictionary<TKey, TValue>) dla .NET 3.5.

+2

Nie ma ich już od 19 października 2012 r. – rickythefox

Powiązane problemy