Параллельный.Задач по каждому элементу против.Фабрика.StartNew
в чем разница между приведенными ниже фрагментами кода? Не будут ли оба использовать потоки threadpool?
например, если я хочу вызвать функцию для каждого элемента в коллекции,
Parallel.ForEach<Item>(items, item => DoSomething(item));
vs
foreach(var item in items)
{
Task.Factory.StartNew(() => DoSomething(item));
}
4 ответа:
первый вариант гораздо лучше.
параллельный.По каждому элементу внутренне, использует
Partitioner<T>
для распределения коллекции по рабочим элементам. Он не будет выполнять одну задачу на элемент, а скорее пакетировать это, чтобы снизить накладные расходы.второй вариант будет планировать один
Task
за элемент в вашей коллекции. Хотя результаты будут (почти) одинаковыми, это приведет к гораздо большим накладным расходам, чем необходимо, особенно для больших коллекций, и заставьте общее время выполнения быть медленнее.FYI - используемый разделитель может управляться с помощью соответствующего перегрузки в параллель.По каждому элементу, если есть такое желание. Дополнительные сведения см. В разделе Пользовательские Разделители на MSDN.
основное отличие, во время выполнения, второй будет действовать асинхронно. Это может быть продублировано с помощью Parallel.По каждому элементу делать:
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
делая это, вы все еще пользуетесь разделителями, но не делаете блокируйте до завершения операции.
Я провел небольшой эксперимент по запуску метода "1000000000" раз с "параллельным".Для "и один с" задачи " объектов.
я измерил время процессора и нашел параллель более эффективной. Параллельный.For делит вашу задачу на небольшие рабочие элементы и выполняет их на всех ядрах параллельно оптимальным образом. При создании большого количества объектов задач ( FYI TPL будет использовать пул потоков внутри) будет перемещать каждое выполнение каждой задачи, создавая больше стресса в поле, что очевидно из эксперимент ниже.
Я также создал небольшое видео, которое объясняет основные TPL, а также продемонстрировал, как параллельно.Для более эффективного использования вашего ядра http://www.youtube.com/watch?v=No7QqSc5cl8 по сравнению с обычными задачами и потоками.
эксперимент 1
Parallel.For(0, 1000000000, x => Method1());
Эксперимент 2
for (int i = 0; i < 1000000000; i++) { Task o = new Task(Method1); o.Start(); }
параллельный.ForEach будет оптимизировать (может даже не начать новые потоки) и блокировать, пока цикл не будет завершен, и задача.Фабрика явно создаст новый экземпляр задачи для каждого элемента и вернет его до завершения (асинхронные задачи). Параллельный.Foreach гораздо эффективнее.
на мой взгляд, наиболее реалистичный сценарий-это когда задачи имеют тяжелую операцию для завершения. Подход Shivprasad направлена больше на создание объекта/выделение памяти, чем на вычислительных себя. Я провел исследование, вызвав следующий метод:
public static double SumRootN(int root) { double result = 0; for (int i = 1; i < 10000000; i++) { result += Math.Exp(Math.Log(i) / root); } return result; }
выполнения этого метода занимает около 0,5 сек.
я назвал его 200 раз, используя параллель:
Parallel.For(0, 200, (int i) => { SumRootN(10); });
затем я назвал его 200 раз, используя старомодный способ:
List<Task> tasks = new List<Task>() ; for (int i = 0; i < loopCounter; i++) { Task t = new Task(() => SumRootN(10)); t.Start(); tasks.Add(t); } Task.WaitAll(tasks.ToArray());
первый случай завершенный в 26656ms, второй в 24478ms. I повторил его много раз. Каждый раз второй подход немного быстрее.