2012-06-01 2 views
5

Возможно, я не понял это правильно ... все проблемы с параллельным классом :(Использование System.Threading.Tasks.Parallel создать новый поток в пуле потоков?

Но из того, что я сейчас читаю, я понимаю, что когда я использую Параллель, я фактически мобилизую все потоки, существующие в threadPool для некоторой задачи/миссии

Например:..

var arrayStrings = new string[1000]; 
    Parallel.ForEach<string>(arrayStrings, someString => 
    { 
     DoSomething(someString); 
    }); 

Так Parallel.ForEach в данном случае мобилизует все нити, которые существуют в Threadpool для «DoSomething» задачи/миссии

Но вызов Parallel.ForEach будет создавать любой новый поток?

Его ясно, что не будет 1000 новых потоков. Но давайте предположим, что есть 1000 новых потоков, в каком случае threadPool выпускает весь поток, который он удерживает так, в этом случае ... Parallel.ForEach создаст любой новый поток?

+0

['Parallel.ForEach'] (http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.foreach.aspx) -« Выполняет foreach (для каждого в Visual Basic), в которой итерации ** могут выполняться параллельно ». –

ответ

10

Короткий ответ: Parallel.ForEach() не «мобилизует все потоки». И любая операция, которая планирует некоторые работы над ThreadPool (что делает Parallel.ForEach()), может вызвать создание нового потока в пуле.

Длинный ответ: Для того, чтобы понять это правильно, вы должны знать, как три уровня абстракции работы: Parallel.ForEach(), TaskScheduler и ThreadPool:

  1. Parallel.ForEach()Parallel.For()) планировать свою работу на TaskScheduler. Если вы не укажете планировщика явно, будет использоваться the current one.

    Parallel.ForEach() разделяет работу между несколькими Task с. Каждый Task обрабатывает часть входной последовательности, и когда это будет сделано, она запросит другую часть, если она доступна, и так далее.

    Сколько Task s будет Parallel.ForEach() создать? Целых, как TaskScheduler позволит запустить. Способ, которым это делается, состоит в том, что каждый Task сначала выдает копию самого себя, когда он начинает выполнение (если это не будет нарушено MaxDegreeOfParallelism, если вы его установили). Таким образом, фактический уровень параллелизма зависит от TaskScheduler.

    Кроме того, первый Task фактически выполнит текущий поток, если его поддерживает TaskScheduler (это делается с использованием RunSynchronously()).

  2. The default TaskScheduler просто вставляет каждому Task в очередь ThreadPool. (На самом деле, это сложнее, если вы начинаете Task с другого Task, но это не имеет значения здесь.) Другие TaskScheduler s могут делать совершенно разные вещи, а некоторые из них (например,) совершенно непригодны для использования с Parallel.ForEach().

  3. ThreadPool использует довольно сложный алгоритм, позволяющий точно определить, сколько потоков должно выполняться в любой момент времени. Но самое главное, что планирование нового рабочего элемента может вызвать создание нового потока (хотя и не обязательно сразу). И поскольку с Parallel.ForEach() всегда есть какой-то элемент в очереди, который должен выполняться, он полностью соответствует внутреннему алгоритму ThreadPool, чтобы определить количество потоков.

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

Но, как правило, должен быть близок к оптимальной эффективности, и вам, вероятно, не придется беспокоиться обо всех этих деталях.

1

Parallel.Foreach не создает новые потоки и не «мобилизует все потоки». Он использует ограниченное количество потоков из threadpool и отправляет задачи для них для параллельного выполнения. В текущей реализации по умолчанию используется один поток на ядро.

+1

Это просто неправда. Если код внутри 'Parallel.ForEach()' блокируется или запускается в течение длительного времени, будет использоваться больше потоков, чем количество ядер. – svick

0

Parallel не занимается нитями вообще - он планирует ЗАДАЧИ в рамки задачи. Тогда у него есть планировщик, а планировщик по умолчанию отправляется в threadpool. Этот попытается найти количество потоков в goo (лучше в 4.5, чем 4.0), и Threadpool может медленно разворачивать новые потоки.

Но это не functoin из Parallel.ForEach;)

Parallel.ForEach создаст любую новую нить ???

Этого никогда не будет. Как я уже сказал, он имеет 1000 foreach, затем он ставит 10.000 задач, Point. Планировщик фабрики задач выполнит то, что он запрограммировал, чтобы сделать ((вы можете его заменить). Обычно по умолчанию - да, медленно появятся новые потоки WITHIN REASON.

+0

'Parallel.ForEach()' в коллекции элементов * n * обычно не создает * n * 'Task', это может быть слишком неэффективно. Он разбивает исходную коллекцию и создает только столько «Задачей, что« TaskScheduler »позволяет ей запускаться. – svick

1

Я думаю, что у вас есть это неправильно. из PATTERNS OF PARALLEL PROGRAMMING вы увидите, что Parallel.ForEach просто очень синтаксический сахар.

Parallel.ForEach в основном свелась к чему-то вроде этого,

for (int p = 0; p < arrayStrings.Count(); p++) 
{ 
    ThreadPool.QueueUserWorkItem(DoSomething(arrayStrings[p]); 
} 

ThreadPool заботится о планировании. есть некоторые отличные статьи о том, как планировщик ThreadPool ведет себя до некоторой степени i Вам интересно, но это не связано с TPL.

+1

'new Thread()' всегда будет создавать новый поток, а не использовать его из threadpool.Введенный вами код всегда будет создавать столько потоков, сколько есть элементов в коллекции. Это никоим образом не означает Parallel.ForEach. –

+0

@AllonGuralnek, вы правы, обновлены. –

+1

'Parallel.ForEach()' не построен поверх 'ThreadPool', он построен поверх' TaskScheduler'. Кроме того, это умнее о коде в каждой «Задаче», так что нет одной «Задачи» для каждого элемента. Другое дело, что ваш код не будет блокироваться, но 'Parallel.ForEach()' делает. – svick

Смежные вопросы