Parallel.ForEach із додаванням до списку


80

Я намагаюся запустити кілька функцій, які підключаються до віддаленого сайту (за мережею) і повертають загальний список. Але я хочу запускати їх одночасно.

Наприклад:

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

На мою думку, одночасне внесення результатів до результатів може відбутися ... Що може призвести до збою моєї програми.

Як я можу цього уникнути?


Яку версію .NET ви використовуєте?
sll

4
Це повинно бути принаймні .Net 4; Там була введена паралель.
Matt Mills

Відповіді:


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

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

В основному блокування означає, що лише один потік може одночасно мати доступ до цього критичного розділу.


1
Але що станеться, якщо ПОКУМИ ці результати додаватимуться результати від іншого постачальника, які намагатимуться додати? вони не зможуть або зачекають, поки це стане можливим?
shaharmor

4
Коли є замок, нитка буде чекати, поки зможе дістати замок.
Гедріан

Так що в основному це як сказати: Зачекайте, поки! Results.isLocked, а коли його безкоштовно заблокуйте і напишіть?
shaharmor

8
Незначний момент: thisце не найбезпечніший вибір для об'єкта блокування. Краще використовувати спеціальний приватний об'єкт: lock(resultsLock).
Henk Holterman

2
locksможе сповільнити загальний час виконання, хоча ... одночасних колекцій, здається, краще уникати цього
Piotr Kula

155

Ви можете використовувати одночасну колекцію .

System.Collections.ConcurrentПростір імена надають кілька класів збору потокобезпечна , які повинні використовуватися замість відповідних типів в System.Collections і System.Collections.Genericпросторах імен , коли кілька потоків отримують доступ до колекції одночасно.

Наприклад, ви можете використовувати, ConcurrentBagоскільки ви не маєте гарантії, в якому порядку будуть додані товари.

Представляє безпечну для потоків невпорядковану колекцію об’єктів.


Так, це фактична відповідь. Ви отримаєте кращу продуктивність (як правило) за допомогою одночасних колекцій.
lkg

32

Для тих, хто віддає перевагу коду:

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

    return results;
}

Доводиться використовувати петлю: foreach (var item in currentProvider.SearchTitle((title))) results.Add(item);
Ентоні Макграт

25

Одночасні колекції нові для .Net 4; вони призначені для роботи з новою паралельною функціональністю.

Див. Одночасні колекції в .NET Framework 4 :

До .NET 4 вам потрібно було надати власні механізми синхронізації, якщо кілька потоків можуть отримувати доступ до однієї спільної колекції. Вам довелося заблокувати колекцію ...

... [нові] класи та інтерфейси в System.Collections.Concurrent [додані в .NET 4] забезпечують послідовну реалізацію [...] багатопотокових проблем програмування, що включають спільні дані між потоками.


14

Це можна коротко виразити за допомогою PLINQ AsParallelта SelectMany:

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

linq selectMany - це чудово, на жаль linq повільніше, ніж звичайний foreach. :(
Куган Кумар,

6
Не мікрооптимізуйте. ОЗ передбачає, що SearchTitleпідключається до віддаленого сайту. Його затримка буде на кілька порядків повільніше, ніж різниця між LINQ і foreach.
Дуглас,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.