PLINQ в ConcurrentQueue не является многопоточным - PullRequest
2 голосов
/ 22 мая 2011

У меня есть следующий оператор PLINQ в программе на C #:

 foreach (ArrestRecord arrest in
            from row in arrestQueue.AsParallel()
            select row)
        {
            Geocoder geocodeThis = new Geocoder(arrest);
            writeQueue.Enqueue(geocodeThis.Geocode());
            Console.Out.WriteLine("Enqueued " + ++k);
        }

И arrestQueue, и writeQueue ConcurrentQueues .

Ничего не выполняется вПараллельно:

  • Во время работы общее использование ЦП составляет около 30%, и это также относится ко всему остальному.У меня 8 ядер (Hyper-Threading на Core i7 720QM с 4 физическими ядрами), и 4 из 8 ядер практически не используются.Остальные работают примерно на 40% -50%.
  • Использование диска обычно составляет 0%, и использование сети отсутствует, за исключением запросов к базе данных Postgres на localhost (см. Ниже).
  • Если ядобавить точку останова где-то внутри geocodeThis.Geocode(), в раскрывающемся списке Thread Visual Studio просто говорится [ pid ] Основной поток .Он никогда не переходит в какой-либо другой поток.
  • Я использую Npgsql для подключения к Postgres, и каждый поток выполняет несколько SELECT запросов к таблице.Я запускаю приложение pgAdmin III «Состояние сервера», которое показывает pg_stat_activity .Наблюдая за этим и размещением стратегических точек останова (см. Выше), я вижу, что приложение никогда не имеет более одного открытого подключения к базе данных для всех якобы одновременных потоков, работающих geocodeThis.Geocode().Даже если я добавлю Pooling = false в строку подключения к БД, чтобы принудительно не объединять подключения, я никогда не увижу более 1 подключения, используемого в geocodeThis.Geocode().
  • Таблица Postgresиндексируется в каждом столбце в предложении WHERE .Даже если бы он был плохо проиндексирован, я бы ожидал много использования диска.Если бы Postgres держал вещи каким-либо другим способом, похоже, что это впитало бы ядро.

Это похоже на простое исследование PLINQ, и я ломаю голову над тем, почему ничего не работает параллельно.

Ответы [ 2 ]

5 голосов
/ 22 мая 2011

Вы распараллеливаете только перечисление самого assertQueue и затем «не распараллеливаете» его обратно в обычный IEnumerable.Все это происходит еще до запуска цикла foreach.Затем вы используете обычный IEnumerable с foreach, который запускает тело цикла последовательно.

Есть много способов запустить тело цикла параллельно, но первый, который приходит на ум, этоиспользуя Parallel.ForEach:

Parallel.ForEach(arrestQueue, arrest =>
    {
        Geocoder geocodeThis = new Geocoder(arrest);
        writeQueue.Enqueue(geocodeThis.Geocode());
        Console.Out.WriteLine("Enqueued " + ++k);
    });
1 голос
/ 22 мая 2011

Foreach для параллельной коллекции по-прежнему является однопоточной операцией. .AsParallel возвращает коллекцию, которая определяет метод .ForAll, который может (но по контракту не всегда) выполняться параллельно. Код для этого будет:

arrestQueue.AsParallel().ForAll(arrest=>
    {
        Geocoder geocodeThis = new Geocoder(arrest);
        writeQueue.Enqueue(geocodeThis.Geocode());
        Console.Out.WriteLine("Enqueued " + ++k);
    });
...