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.