Jakiś czas temu w pracy miałam ciekawy przypadek. Mianowicie – losowo wywalał się jeden z testów. Powodem był NullReferenceException w logice. Żeby dojść do problemu, trzeba było przeanalizować trochę kodu.
Parallel.ForEach()
Zauważyłam, że w pewnym miejscu została użyta pętla Parallel.ForEach(). Dla uproszczenia załóżmy taką logikę:
List<object> myList = new List<object>();
Parallel.ForEach(Enumerable.Range(1, 10), i =>
{
var items = GetItems(i);
myList.AddRange(items);
});
Bardzo fajnie, że zamiast zwykłej pętli foreach() na jednym wątku, można zrównoleglić pracę i użyć Parallel.ForEach(). Przykład z Internetów iterujący po 10 elementach pokazuje, że wydajnościowo się to opłaca:

No i super. To w czym tkwi problem?
List<T> vs ConcurrentBag<T>
Problemem jest nieszczęsna lista obiektów List<object> i operacja dodawanie do niej nowych elementów – a więc metoda AddRange(). Zwykła lista nie jest thread safe. W większości przypadków nie było problemów z dostępem do listy i test przechodził. Ale czasami coś poszło nie tak i lista zawierała nulle. Dalsze operacje właśnie na tych nullach powodowały NullReferenceException.
I tutaj wjeżdża na białym koniu nasze rozwiązanie – ConcurrentBag. Thread safe, nieuporządkowana kolekcja. Wystarczy dodać odpowiedni using System.Collections.Concurrent i śmiga:
ConcurrentBag<object> myList = new ConcurrentBag<object>();
Parallel.ForEach(Enumerable.Range(1, 10), i =>
{
var items = GetItems(i);
items.ForEach(item => myList.Add(item));
});
I w ten sposób naprawiłam buga 🙂
Macie jakieś ciekawe bugi, które naprawiliście? Podzielcie się!
Podoba Ci się to, co tworzę? Chcesz dostawać informacje o:
– wydarzeniach, które organizuję lub wspieram (np. konferencje, meetupy, webinary)
– inicjatywach, które organizuję lub wspieram (np. GeekWeekWro, DevAdventCalendar)
– moich prelekcjach, kursach i szkoleniach
– wyróżnionych artykułach z mojego bloga
0% SPAMu, 100% informacji! Krótko i na temat.
Przydałby się jeszcze wynik testu na wydajności ConcurrentBag. Kolekcje Concurrent* są wolniejsze – synchronizacja jest kosztowna.
Po za tym fajnie że ktoś o tym problemie co jakiś czas przypomina.
PolubieniePolubione przez 1 osoba
Trafna uwaga. Przykładowy wynik testu wydajności można znaleźć pod linkiem https://stackoverflow.com/a/35417520
PolubieniePolubienie