Rozważ poniższy słownik

var dic = new ConcurrentDictionary<Guid, List<Customer>>();

Jeśli zrobię równolegle. Dla i dodaj elementy do tego słownika, wyniki są nieprzewidywalne. Czasami elementy na liście są null, a czasami są wyjątki.

var id = Guid.NewGuid();
Parallel.For(0, 1000, _ =>
{
    dic.AddOrUpdate(id,new List<Customer>(), (key, lst) =>
    {
        var c = new Customer()
        {
             Id = key
        };
        lst.Add(c);
        return lst;
    });
});

Rozumiem tę listę.add nie jest bezpieczni nicią, więc powoduje problem. Mogę go naprawić za pomocą koncentrycznego zamiast listy.

Jednak próbuję zrozumieć, dlaczego przedmiot słownika musi być bezpieczny, gdy słownik jest sejfem? Czy współbieżny słownik nie blokuje się już, gdy element jest dodawany lub aktualizowany?

0
user3587180 21 luty 2019, 00:43

2 odpowiedzi

Najlepsza odpowiedź

Z dokumentacji MSDN tutaj (kopalnia nacisk): https://docs.Microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.addorupdate?view=netFramework-4.7.2 .

W przypadku modyfikacji i operacji zapisu do słownika koncentrygnowanie wykorzystuje drobnoziarnisty blokowanie, aby zapewnić bezpieczeństwo wątków. (Operacje odczytu na słowniku wykonywane są w sposób wolny od blokady.) Jednak delegat aktualizacjiVAgaluefactory nazywany jest poza zamkami , aby uniknąć problemów, które mogą wynikać z wykonywania nieznanego kodu pod blokadą. Dlatego addoupdate nie jest atomowy w odniesieniu do wszystkich innych operacji na klasie koncentransującej.

2
Chris 20 luty 2019, 21:52

Blokada koncentriencyjna nie zaoszczędzi w następnym scenariuszu:

Wątek1:

List<Customer> c = ConcurrentDictionary[{guid}];
Thread.Sleep(1000);

Wątek2:

List<Customer> c2 = ConcurrentDictionary[{same guid}];
c2.Add(...);

Wątek1:

c.Add(...);
0
Steve 20 luty 2019, 21:52