2013-03-20 3 views
36

Я уже читал предыдущие вопросы здесь о ConcurrentBag, но не нашел фактического примера реализации в многопоточности.Какое правильное использование ConcurrentBag?

ConcurrentBag является поточно-реализацией мешка, оптимизированная для сценариев, в которых тот же поток будет как производства и потребление данных, хранящихся в сумке.»

В настоящее время это текущее использование в моем коде (это упрощено не фактические коды):

private void MyMethod() 
{ 
    List<Product> products = GetAllProducts(); // Get list of products 
    ConcurrentBag<Product> myBag = new ConcurrentBag<Product>(); 

    //products were simply added here in the ConcurrentBag to simplify the code 
    //actual code process each product before adding in the bag 
    Parallel.ForEach(
       products, 
       new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, 
       product => myBag.Add(product)); 

    ProcessBag(myBag); // method to process each items in the concurrentbag 
} 

Мои вопросы:
является ли это право использования ConcurrentBag это нормально использовать ConcurrentBag в этом вид сценария?

Для меня я думаю, что простой List<Product> и ручной блокировки сделают лучше. Причиной этого является то, что описанный выше сценарий уже разрушает «, тот же поток будет производить и потреблять данные, хранящиеся в сумме».
Также я обнаружил, что хранилище ThreadLocal, созданное в каждом потоке в параллеле, будет существовать после операции (даже если поток используется повторно, это правильно?), Что может вызвать утечку нежелательной памяти.
Я прямо в этом ребята? Или достаточно простого или чистого метода удаления элементов в ConcurrentBag?

+0

Я думаю, что накладные расходы и общая производительность в этом случае будут больше, чем при использовании синхронного подхода. Вы это оценили? –

+1

ConcurrentBag имеет конструктор, который принимает 'IEnumerable ' 'var myBag = new ConcurrentBag (продукты);' – Romoku

+0

Привет, я имею в виду, что я буду по-прежнему почти таким же кодом выше, за исключением того, что я буду заменять ConcurrentBag со списком и добавьте блокировку при добавлении продукта в список внутри Parallel.ForEach – hisoka21

ответ

13

Это похоже на использование ConcurrentBag. Локальные переменные потока являются членами пакета и будут иметь право на сбор мусора в то же время, когда сумка (очистка содержимого не освобождает их). Вы правы, что для вашего случая достаточно простого списка с блокировкой. Если работа, которую вы выполняете в цикле, имеет существенное значение, тип синхронизации потоков не будет иметь большого значения для общей производительности. В этом случае вам будет удобнее использовать то, с чем вы знакомы.

Другим вариантом было бы использовать ParallelEnumerable.Select, что соответствует тому, что вы пытаетесь сделать более внимательно. Опять же, любая разница в производительности, которую вы собираетесь увидеть, скорее всего, будет незначительной, и нет ничего плохого в том, чтобы придерживаться того, что вы знаете.

Как всегда, если выполнение этого является критическим, его нельзя заменить и измерить.

0

Мне кажется, что bmm6o неверен. Экземпляр ConcurrentBag внутренне содержит мини-пакеты для каждого потока, который добавляет к нему элементы, поэтому вставка элемента не включает никаких блокировок потоков, и поэтому все потоки Environment.ProcessorCount могут полностью размахивать, не зацикливаясь на ожидании и без каких-либо переключателей контекста потока. Синхронизация нити может потребоваться при повторении по собранным элементам, но опять же в исходном примере итерация выполняется одним потоком после завершения всех вставок. Более того, если ConcurrentBag использует методы блокировки как первый уровень синхронизации потоков, то вообще можно вообще не включать операции мониторинга.

С другой стороны, с использованием обычного экземпляра List<T> и упаковки каждого его метода вызова Add() с ключевым словом блокировки сильно повредит производительность. Во-первых, из-за постоянных вызовов Monitor.Enter() и Monitor.Exit(), каждый из которых требует глубокого углубления в режим ядра и для работы с примитивами синхронизации Windows. Во-вторых, иногда иногда один поток может быть заблокирован вторым потоком, потому что второй поток еще не завершил его добавление.

Как для меня, приведенный выше код является действительно хорошим примером правильного использования класса ConcurrentBag.