Когда Parallel.Invoke полезен? - PullRequest
       6

Когда Parallel.Invoke полезен?

2 голосов
/ 22 августа 2011

Я просто погружаюсь в изучение класса Parallel в рамках 4.0 и пытаюсь понять, когда это будет полезно.Сначала, просмотрев некоторую документацию, я попытался выполнить два цикла, один с использованием Parallel.Invoke, а другой последовательно, например:

static void Main()
{
    DateTime start = DateTime.Now;
    Parallel.Invoke(BasicAction, BasicAction2);
    DateTime end = DateTime.Now;
    var parallel = end.Subtract(start).TotalSeconds;

    start = DateTime.Now;
    BasicAction();
    BasicAction2();
    end = DateTime.Now;
    var sequential = end.Subtract(start).TotalSeconds;

    Console.WriteLine("Parallel:{0}", parallel.ToString());
    Console.WriteLine("Sequential:{0}", sequential.ToString());
    Console.Read();
}
static void BasicAction()
{
    for (int i = 0; i < 10000; i++)
    {
        Console.WriteLine("Method=BasicAction, Thread={0}, i={1}", Thread.CurrentThread.ManagedThreadId, i.ToString());
    }
}

static void BasicAction2()
{
    for (int i = 0; i < 10000; i++)
    {
       Console.WriteLine("Method=BasicAction2, Thread={0}, i={1}", Thread.CurrentThread.ManagedThreadId, i.ToString());
    }
}

Здесь нет заметной разницы во времени выполнения, или я пропустилточка?Это более полезно для асинхронных вызовов веб-служб или ...?

РЕДАКТИРОВАНИЕ: Я удалил DateTime с помощью секундомера, удалил запись в консоль с помощью простой операции добавления.

ОБНОВЛЕНИЕ - Большая разница во времени сейчас: Спасибодля устранения проблем, возникших у меня при подключении консоли

static void Main()
{
    Stopwatch s = new Stopwatch();
    s.Start();
    Parallel.Invoke(BasicAction, BasicAction2);
    s.Stop();
    var parallel = s.ElapsedMilliseconds;

    s.Reset();
    s.Start();
    BasicAction();
    BasicAction2();
    s.Stop();

    var sequential = s.ElapsedMilliseconds;

    Console.WriteLine("Parallel:{0}", parallel.ToString());
    Console.WriteLine("Sequential:{0}", sequential.ToString());
    Console.Read();
}

static void BasicAction()
{
    Thread.Sleep(100);

}

static void BasicAction2()
{
    Thread.Sleep(100);
}

Ответы [ 4 ]

6 голосов
/ 22 августа 2011

Тест, который вы проводите, бессмысленен; вы проверяете, работает ли что-то, что вы не можете выполнить параллельно, быстрее, если вы выполняете это параллельно.

Console.Writeline обрабатывает синхронизацию для вас, поэтому она всегда будет действовать так, как будто она работает в одном потоке.

С здесь :

... вызвать метод SetIn, SetOut или SetError соответственно. I / O операции с использованием этих потоков синхронизируются, что означает потоки могут читать или записывать потоки.

Любое преимущество, которое получает параллельная версия от работы в нескольких потоках, теряется из-за маршалинга, выполняемого консолью. На самом деле я не удивлюсь, увидев, что переключение всех потоков фактически означает, что параллельный прогон будет медленнее .

Попробуйте сделать что-то еще в действиях (простой Thread.Sleep), чтобы мог обрабатываться несколькими потоками одновременно, и вы должны увидеть большую разницу во времени выполнения. Достаточно большой, чтобы неточность использования DateTime в качестве механизма синхронизации не будет иметь большого значения.

2 голосов
/ 22 августа 2011

Это не вопрос времени исполнения. Вывод на консоль определяется тем, как запланированы действия. Чтобы получить точное время выполнения, вы должны использовать StopWatch . В любом случае, вы используете Console.Writeline, поэтому он будет выглядеть так, как будто он находится в одном потоке выполнения. Любая вещь, которую вы пытались достичь с помощью parallel.invoke, теряется природой Console.Writeline.

1 голос
/ 22 августа 2011

На чем-то простом время выполнения будет таким же.Parallel.Invoke выполняет два метода одновременно.

В первом случае у вас будут строки в консоли в смешанном порядке.

Method=BasicAction2, Thread=6, i=9776
Method=BasicAction, Thread=10, i=9985
// <snip>
Method=BasicAction, Thread=10, i=9999
Method=BasicAction2, Thread=6, i=9777

Во втором случае у вас будут все действия BasicAction до BasicAction2.

Это показывает, что оба метода работают одновременно.

0 голосов
/ 22 августа 2011

В идеальном случае (если число делегатов равно количеству параллельных потоков и имеется достаточное количество ядер процессора), длительность операций станет MAX(AllDurations) вместо SUM(AllDurations) (если AllDurations - список выполнения каждого делегата) время как {1сек, 10сек, 20сек, 5сек}). В менее идеальном случае он движется в этом направлении.

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

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

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

...