PLINQ: как запустить ParallelQuery на более чем 4 потоках? - PullRequest
5 голосов
/ 27 июля 2011

Обновление - изменил название вопроса, чтобы отразить то, что я действительно после

Рассмотрим следующий фрагмент кода:

// this query generates 12 instances of Func<int>, which each when executed
// print something to the console and wait for 1 second.
var actions = Enumerable.Range(0, 12).Select(i => new Func<int>(() =>
{
    Console.WriteLine("{0} - waiting 1 sec", i);
    Thread.Sleep(1000);
    return 1;
}));

// define a parallel query. Note the WithDegreeOfParallelism call here.
var query = from action in actions.AsParallel().WithDegreeOfParallelism(12)
            select action();

// execute, measuring total duration
var stopw = Stopwatch.StartNew();
query.ToList();
Console.WriteLine(stopw.Elapsed);
Console.WriteLine(Environment.ProcessorCount); // 3 on my machine

При пропуске вызова на WithDegreeOfParallelism это выполняется в 4 чанках, что занимает в общей сложности около 4 секунд, чего я и ожидал, так как мой процессор равен 3.

Однако, когда я звоню WithDegreeOfParallelism с любого номера выше 4, я всегда получаю 3 чанка, и общая продолжительность не превышает 3 секунд. Я ожидаю, что значение 12 получит общую продолжительность (чуть больше) 1 секунды.

Что мне не хватает? И как я могу обеспечить параллельное выполнение более 4 задач, не требующих интенсивной работы процессора, что мне и нужно?

Обновление: я, конечно, мог бы вернуться к ручному раскручиванию потоков, но я надеялся, что новая библиотека PFX сделает это немного проще ... В любом случае, приведенный ниже код дает мне около 1 секунды общего времени выполнения

List<Thread> threads = new List<Thread>();
for (int i = 0; i < 12; i++)
{
    int i1 = i;
    threads.Add(new Thread(() =>
    {
        Console.WriteLine(i1);
        Thread.Sleep(1000);
    }));
}
threads.ForEach(t => t.Start());
threads.ForEach(t => t.Join());

Ответы [ 3 ]

3 голосов
/ 27 июля 2011

Попробуйте запустить новые задачи в параллельном цикле с параметром TaskCreationOptions.LongRunning.Они начнутся сразу, вместо того, чтобы ждать, пока поток в пуле потоков станет доступным.

2 голосов
/ 28 июля 2011

WithDegreeOfParallelism определяет, сколько задач должно создать PLINQ, но необязательно, сколько потоков будет использовано.

Поскольку задачи выполняются как рабочие элементы в ThreadPool, количество потоков, выполняющих запрос, будет ограничено размером ThreadPool. ThreadPool будет добавлять потоки по мере необходимости, но это может занять некоторое время - ThreadPool может добавлять 2 потока в секунду или около того.

Если вы хотите быстро добавить темы в ThreadPool, вы можете использовать метод SetMinThreads. Если вы поместите этот код в начало кода, тест должен завершиться примерно через секунду:

int prevThreads, prevPorts;
ThreadPool.GetMinThreads(out prevThreads, out prevPorts);
ThreadPool.SetMinThreads(12, prevPorts);

Вы можете решить, сколько потоков вам нужно, а затем использовать SetMinThreads и SetMaxThreads, чтобы установить границы для размера ThreadPool.

2 голосов
/ 27 июля 2011

Как я уже сказал, WithDegreeOfParallelism устанавливает только верхнюю границу. Попробуйте увеличить ваши задачи с 10 до 100. В итоге у вас будет около 10 секунд на все 100 из них.Ваш код подходит для большего числа задач, выполняющих меньшие операции.и добавьте Console.WriteLine("{0} threads " ,Process.GetCurrentProcess().Threads.Count); в свою задачу, чтобы увидеть, сколько потоков создано. (Количество потоков не является числом потоков, созданных plinq. Посмотрите, как оно увеличивается).параллелизм с PLinq.Прочитайте эту статью http://msdn.microsoft.com/en-us/library/dd997411.aspx. Вам нужно выбрать лучший способ для соответствующего требования, чтобы получить лучшую производительность.

...