Параллельный.Задач по каждому элементу против.Фабрика.StartNew


в чем разница между приведенными ниже фрагментами кода? Не будут ли оба использовать потоки threadpool?

например, если я хочу вызвать функцию для каждого элемента в коллекции,

Parallel.ForEach<Item>(items, item => DoSomething(item));

vs

foreach(var item in items)
{
  Task.Factory.StartNew(() => DoSomething(item));
}
4 234

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();
}

Processor time comparison

параллельный.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 повторил его много раз. Каждый раз второй подход немного быстрее.