Parallel.ForEach z dodawaniem do listy


80

Próbuję uruchomić wiele funkcji, które łączą się ze zdalną lokalizacją (przez sieć) i zwracają listę ogólną. Ale chcę je uruchamiać jednocześnie.

Na przykład:

public static List<SearchResult> Search(string title)
{
    //Initialize a new temp list to hold all search results
    List<SearchResult> results = new List<SearchResult>();

    //Loop all providers simultaneously
    Parallel.ForEach(Providers, currentProvider =>
    {
        List<SearchResult> tmpResults = currentProvider.SearchTitle((title));

        //Add results from current provider
        results.AddRange(tmpResults);
    });

    //Return all combined results
    return results;
}

Jak widzę, wiele wstawień do „wyników” może mieć miejsce w tym samym czasie ... co może spowodować awarię mojej aplikacji.

Jak mogę tego uniknąć?


Której wersji .NET używasz?
SLL

4
Musiałby to być co najmniej .Net 4; Wprowadzono tam paralelę.
Matt Mills

Odpowiedzi:


58
//In the class scope:
Object lockMe = new Object();    

//In the function
lock (lockMe)
{    
     results.AddRange(tmpResults);
}

Zasadniczo blokada oznacza, że ​​tylko jeden wątek może mieć dostęp do tej krytycznej sekcji w tym samym czasie.


1
Ale co się stanie, jeśli PODCZAS dodawania tych wyników, spróbują dodać wyniki od innego dostawcy? czy ZAWODĄ, czy CZEKA, aż będzie to możliwe?
shaharmor

4
Kiedy pojawi się blokada, wątek będzie czekał, aż będzie mógł go uzyskać.
Haedrian

Więc w zasadzie to tak, jakby powiedzieć: Poczekaj, aż wyniki.
shaharmor

8
Drobna uwaga: thisnie jest najbezpieczniejszym wyborem dla obiektu zamka. Lepiej użyć specjalnego prywatny obiekt: lock(resultsLock).
Henk Holterman

2
locksmoże jednak spowolnić ogólny czas realizacji .. równoległe kolekcje wydają się lepsze, aby tego uniknąć
Piotr Kula

155

Możesz użyć kolekcji współbieżnej .

Przestrzeń System.Collections.Concurrentnazw udostępnia kilka klas kolekcji bezpiecznych dla wątków, które powinny być używane zamiast odpowiednich typów w przestrzeniach nazw System.Collections i, System.Collections.Genericgdy wiele wątków jednocześnie uzyskuje dostęp do kolekcji.

Możesz na przykład użyć, ConcurrentBagponieważ nie masz gwarancji, w jakiej kolejności pozycje zostaną dodane.

Reprezentuje bezpieczną wątkowo, nieuporządkowaną kolekcję obiektów.


Tak, to jest właściwa odpowiedź. Uzyskasz lepszą wydajność (ogólnie) przy jednoczesnych kolekcjach.
lkg

32

Dla tych, którzy wolą kod:

public static ConcurrentBag<SearchResult> Search(string title)
{
    var results = new ConcurrentBag<SearchResult>();
    Parallel.ForEach(Providers, currentProvider =>
    {
        results.Add(currentProvider.SearchTitle((title)));
    });

    return results;
}

Muszę użyć pętli: foreach (var item in currentProvider.SearchTitle((title))) results.Add(item);
Anthony McGrath

25

Kolekcje współbieżne są nowością w .Net 4; są zaprojektowane do pracy z nową funkcją równoległą.

Zobacz kolekcje współbieżne w .NET Framework 4 :

Przed .NET 4 trzeba było zapewnić własne mechanizmy synchronizacji, jeśli wiele wątków mogło uzyskiwać dostęp do jednej udostępnionej kolekcji. Trzeba było zablokować kolekcję ...

... [...] [nowe] klasy i interfejsy w System.Collections.Concurrent [dodane w .NET 4] zapewniają spójną implementację dla [...] wielowątkowych problemów programistycznych obejmujących współdzielone dane między wątkami.


14

Można to zwięźle wyrazić za pomocą PLINQ AsParalleli SelectMany:

public static List<SearchResult> Search(string title)
{
    return Providers.AsParallel()
                    .SelectMany(p => p.SearchTitle(title))
                    .ToList();
}

linq selectMany jest świetny, niestety linq jest wolniejszy niż zwykle foreach. :(
Kugan Kumar

6
Nie mikrooptymalizuj. OP sugerował, że SearchTitlełączy się ze zdalną lokalizacją. Jego opóźnienie będzie o kilka rzędów wielkości wolniejsze niż różnica między LINQ i foreach.
Douglas
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.