Как я могу ограничить Parallel.ForEach?

295

У меня есть асинхронный цикл Parallel.ForEach (), с помощью которого я загружаю некоторые веб-страницы. Моя пропускная способность ограничена, поэтому я могу загружать только x страниц за раз, но Parallel.ForEach выполняет весь список нужных веб-страниц.

Есть ли способ ограничить номер потока или любой другой ограничитель при запуске Parallel.ForEach?

Демо-код:

Parallel.ForEach(listOfWebpages, webpage => {
  Download(webpage);
});

Настоящая задача не имеет ничего общего с веб-страницами, поэтому креативные решения для сканирования веб-страниц не помогут.

eugeneK
источник
@jKlaus Если список не изменен, например, это просто набор URL-адресов, я не вижу проблемы?
Шив
@Shiv, если у вас будет достаточно времени ... Подсчитайте количество казней и сравните его с количеством в списке.
jKlaus
@jKlaus Что вы говорите, пойдет не так?
Шив
1
@jKlaus вы модифицируете не поточный безопасный элемент (целое число). Я ожидал бы, что это не будет работать в этом сценарии. OP, с другой стороны, не модифицирует ничего, что должно быть поточно-ориентированным.
Шив
2
@jKlaus Вот пример Parallel.ForEach, который правильно устанавливает счет> dotnetfiddle.net/moqP2C . Ссылка MSDN: msdn.microsoft.com/en-us/library/dd997393(v=vs.110).aspx
jhamm,

Ответы:

564

Вы можете указать MaxDegreeOfParallelismв ParallelOptionsпараметре:

Parallel.ForEach(
    listOfWebpages,
    new ParallelOptions { MaxDegreeOfParallelism = 4 },
    webpage => { Download(webpage); }
);

MSDN: Parallel.ForEach

MSDN: ParallelOptions.MaxDegreeOfParallelism

Николас Батлер
источник
59
Это может не относиться к этому конкретному случаю, но я решил выбросить его на тот случай, если кто-нибудь задумается над этим и сочтет его полезным. Здесь я использую 75% (округлено) от количества процессоров. var opts = new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 1.0)) };
jKlaus
4
Просто для того, чтобы избавить кого-либо от необходимости искать его в документации, передача значения -1аналогична тому, что вообще его не указывается: «Если [значение] равно -1, количество одновременно выполняемых операций не ограничено»
Stuartd
Мне не понятно из документации - означает ли установка MaxDegreeOfParallelism равным 4 (например), означает ли это, что будет 4 потока, каждый из которых выполняет 1/4 части итераций цикла (один раунд из 4 отправленных потоков), или каждый поток по-прежнему выполняет один цикл итерации и мы просто ограничиваем, сколько работает параллельно?
Хашман
7
Чтобы быть ясно, ядра и темы не одно и то же. В зависимости от ЦП число потоков на ядро ​​может быть разным, обычно 2 на ядро. Например, если у вас 4-ядерный процессор с 2 потоками на ядро, то у вас максимум 8 потоков. Чтобы настроить комментарий @jKlaus var opts = new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 2.0)) };. Ссылка на темы против ядер - askubuntu.com/questions/668538/…
TheMiddleMan
41

Вы можете использовать ParallelOptions и установить MaxDegreeOfParallelism, чтобы ограничить количество одновременных потоков:

Parallel.ForEach(
    listOfwebpages, 
    new ParallelOptions{MaxDegreeOfParallelism=2}, 
    webpage => {Download(webpage);});     
rikitikitik
источник
21

Используйте другую перегрузку, Parallel.Foreachкоторая принимает ParallelOptionsэкземпляр, и установите MaxDegreeOfParallelismдля ограничения количества экземпляров, выполняемых параллельно.

Ричард
источник
11

И для пользователей VB.net (синтаксис странный и трудно найти) ...

Parallel.ForEach(listOfWebpages, New ParallelOptions() With {.MaxDegreeOfParallelism = 8}, Sub(webpage)
......end sub)  
user3496060
источник