Parallel.ForEach
- это способ увеличить параллелизм в вашем коде. lock
стремится к снижению параллелизма в вашем коде 1 . Таким образом, редко бывает правильным объединить их 2 .
Как Олегл предложил , параллельные коллекции могут быть одним из способов, чтобы избежать здесь lock
.
Другим интересным подходом было бы использовать PLINQ здесь вместо Parallel.ForEach
. Уже 2019 год, что интересного в написании очередного цикла?
Это могло бы сделать что-то вроде этого:
secondList.AddRange(list.AsParallel.Where(item =>
{
//consider other processes works in here
return item.Active;
});
Это позволяет вам сохранить не-поточно-безопасную коллекцию secondList
, но при этом не беспокоиться о блокировках - потому что это ваш собственный существующий поток, вызывающий AddRange
, который в итоге потребляет IEnumerable<T>
, который предлагает PLINQ; так что только один поток добавляет элементы в коллекцию.
PLINQ пытается настроить параметры буферизации, но может не выполнить достаточно хорошую работу, в зависимости от размера ввода list
и количества потоков, которые он выберет для использования. Если вы недовольны каким-либо ускорением (или не достигли его), попробуйте поиграть с методами WithXxx
, которые он предлагает, прежде чем списывать его.
Если бы у меня было , чтобы выбрать между вашими двумя примерами (при условии, что они оба верны), я бы выбрал вариант 2, потому что он работает на меньше , удерживая замок, который горячо оспаривается всеми другими работниками.
1 Если вы не знаете, что все запрошенные блокировки достаточно тонки, чтобы никакие два параллельных потока не пытались получить одну и ту же блокировку. Но если мы знаем это, почему мы снова используем замки?
2 И поэтому я выхожу на конечность и говорю, что «всегда» неправильно комбинировать их, когда все параллельные блокировки находятся на одном и том же объекте блокировки, , если не будет значительным обработка происходит параллельно вне lock
.