Współbieżny słownik Prawidłowe użycie

84

Czy mam rację sądząc, że jest to właściwe użycie słownika współbieżnego

private ConcurrentDictionary<int,long> myDic = new ConcurrentDictionary<int,long>();

//Main thread at program startup

for(int i = 0; i < 4; i++)
{
  myDic.Add(i, 0);
}

//Seperate threads use this to update a value

myDic[InputID] = newLongValue;

Nie mam blokad itp. I właśnie aktualizuję wartość w słowniku, mimo że wiele wątków może próbować zrobić to samo.

Jon
źródło
2
To zależy - czy newLongValuezależy od poprzedniej wartości myDic[InputID]?
Damien_The_Unbeliever
3
należy unikać bezpośredniego dostępu za pomocą klucza w myDic[InputID]celu sprawdzenia stanu wyścigu. Powinieneś spróbowaćGetOrAdd
Olivier Albertini
3
@OlivierAlbertini, nie sądzę, że myDic[InputID]powoduje to żaden problem, gdy jest używany jako lwartość. GetOrAddnie jest poprawnym zamiennikiem, ponieważ dodaje tylko wtedy, gdy wartość nie istnieje. Zamiast tego możemy użyć, AddOrUpdateaby dodać / zaktualizować tę samą wartość w słowniku.
Jatin Sanghvi

Odpowiedzi:

75

To zależy od tego, co masz na myśli, mówiąc o bezpieczeństwie wątkowym.

Z MSDN - instrukcje : dodawanie i usuwanie elementów z ConcurrentDictionary :

ConcurrentDictionary<TKey, TValue>jest przeznaczony do scenariuszy wielowątkowych. Nie musisz używać blokad w swoim kodzie, aby dodawać lub usuwać elementy z kolekcji. Jednak zawsze jest możliwe, aby jeden wątek mógł pobrać wartość, a inny wątek natychmiast zaktualizować kolekcję, nadając temu samemu kluczowi nową wartość.

W ten sposób można uzyskać niespójny widok wartości elementu w słowniku.

Oded
źródło
2
To interesujący punkt! Czy nadal używałbyś zamka w tym scenariuszu?
Jon
@Jon - To zależy od twojej aplikacji i czy to w porządku. Ale powiedziałbym, że jeśli chcesz uzyskać spójne widoki elementów, musisz opakować każdy odczyt i aktualizację elementu w kłódkę.
Oded
12
Myślę, że to nie jest to, co mówi doktor. Niespójność ma związek z tym, co zawiera pogląd, jeśli pogląd jest tylko wartością, to jest on całkowicie spójny. Dopóki uzyskujesz wartość klucza, wartość klucza w słowniku może się zmienić. Jest to tak samo niespójne jak wartość DateTime.Now.
George Mavritsakis
4

Najlepszym sposobem, aby się tego dowiedzieć, jest zapoznanie się z dokumentacją MSDN.

W przypadku ConcurrentDictionary strona to http://msdn.microsoft.com/en-us/library/dd287191.aspx

W sekcji dotyczącej bezpieczeństwa wątków znajduje się informacja: „Wszyscy publiczni i chronieni członkowie ConcurrentDictionary (Of TKey, TValue) są bezpieczni wątkowo i mogą być używane jednocześnie z wielu wątków”.

Więc z punktu widzenia współbieżności wszystko jest w porządku.

Erdem
źródło
2

Tak masz rację.

To oraz możliwość wyliczenia słownika w jednym wątku podczas zmiany go w innym wątku to jedyne środki do istnienia dla tej klasy.

Jan
źródło
9
Dodam, że tutaj są pomocne informacje, jak i kiedy używać ConcurrentDictionary.
alex. B
1

To zależy, w moim przypadku wolę tę metodę.

ConcurrentDictionary<TKey, TValue>.AddOrUpdate Method (TKey, Func<TKey, TValue>, Func<TKey, TValue, TValue>);

Zobacz bibliotekę MSDN, aby uzyskać szczegółowe informacje dotyczące użycia metody.

Przykładowe użycie:

results.AddOrUpdate(
  Id,
  id => new DbResult() {
     Id = id,
     Value = row.Value,
     Rank = 1
  },
  (id, v) =>
  {
     v.Rank++;
     return v;
  });
Onur
źródło
2
FYI: „Kiedy dostarczysz metodę fabryki wartości (do metod GetOrAdd i AddOrUpdate), może ona faktycznie działać i później zostać odrzucona (ponieważ inny wątek wygrał wyścig).” Więcej informacji tutaj: arbel.net/2013/02/03/…
keremispirli
Tak, masz rację, jak zauważono w sekcji uwag „Jeśli wywołujesz AddOrUpdate jednocześnie w różnych wątkach, addValueFactory może być wywoływana wiele razy, ale jej para klucz / wartość może nie zostać dodana do słownika przy każdym wywołaniu”. Musisz więc mieć pewność, że nie generujesz wielu trwałych obiektów.
Onur
A jeśli potrzebujesz zaktualizować zawartość, nie zmieniając całkowicie przechowywanego obiektu, na przykład, aby zmienić właściwość wcześniej dodanego obiektu, ta metoda jest przydatna, w przeciwnym razie musisz użyć blokad lub innych metod synchronizacji.
Onur
1

Tylko uwaga: nie uzasadnia używania obiektu ConcurrentDicitonary z pętlą liniową, co sprawia, że ​​jest on niedostatecznie wykorzystywany. Najlepszą alternatywą jest postępowanie zgodnie z zaleceniami Dokumentacji Microsoft, o których wspomniał Oded używając Parallelism, zgodnie z poniższym przykładem:

Parallel.For(0, 4, i => 
{
   myDic.TryAdd(i, 0);
});
Antonio Leonardo
źródło