Учитывает ли Параллельная библиотека задач (или PLINQ) другие процессы?

StackOverflow https://stackoverflow.com/questions/3488381

  •  28-09-2019
  •  | 
  •  

Вопрос

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

Например:

У меня есть около 100 медиафайлов, которые необходимо закодировать или перекодировать (напримериз WAV во FLAC или из FLAC в MP3).Кодирование выполняется путем запуска внешнего процесса (например,FLAC.EXE или LAME.EXE).Каждый файл занимает около 30 секунд.Каждый процесс в основном привязан к процессору, но в нем есть некоторый ввод-вывод.У меня 4 ядра, так что в худшем случае (перекодирование путем подключения декодера к кодировщику) по-прежнему используется только 2 ядра.Я бы хотел сделать что-то вроде:

Parallel.ForEach(sourceFiles,
    sourceFile =>
        TranscodeUsingPipedExternalProcesses(sourceFile));

Запустит ли это 100 задач (и, следовательно, 200 внешних процессов, конкурирующих за центральный процессор)?Или он увидит, что процессор занят, и будет выполнять только 2-3 операции одновременно?

Это было полезно?

Решение

Здесь вы столкнетесь с парой проблем.Механизм предотвращения "голодания" планировщика будет рассматривать ваши задачи как заблокированные, поскольку они ожидают выполнения процессами.Вам будет трудно отличить заблокированный поток от потока, просто ожидающего завершения процесса.В результате он может запланировать новые задачи, если ваши задачи выполняются в течение длительного времени (см. Ниже).Эвристика восхождения на холм должна учитывать общую нагрузку на систему, как от вашего приложения, так и от других.Он просто пытается максимизировать выполняемую работу, поэтому будет добавлять больше работы до тех пор, пока общая пропускная способность системы не перестанет увеличиваться, а затем он отключится.Я не знаю подумай это повлияет на ваше приложение, но, вероятно, повлияет и проблема с предотвращением сбоев.

Вы можете найти более подробную информацию о том, как все это работает, в Параллельное программирование с помощью Microsoft®.NET, Колин Кэмпбелл, Ральф Джонсон, Эйд Миллер, Стивен Тауб (более ранний черновик В сети).

"Пул потоков .NET автоматически управляет количеством рабочих потоков в пуле.Он добавляет и удаляет потоки в соответствии со встроенной эвристикой.Пул потоков .NET имеет два основных механизма для внедрения потоков:а голодание-избегание механизм, который добавляет работника потоки, если он видит прогресс в очереди, и hillclimbing эвристика, которая пытается максимизировать пропускную способность при использовании в качестве как можно меньше нитей.

Цель предотвращения голодания - предотвратить тупиковую ситуацию.Такого рода взаимоблокировка может возникнуть, когда рабочий поток ожидает синхронизации событие, которое может быть выполнено только рабочим элементом, который все еще находится в ожидании в глобальных или локальных очередях пула потоков.Если бы существовало фиксированное количество рабочих потоков, и все эти потоки были бы аналогично заблокированы, система никогда не смогла бы добиться дальнейшего прогресса.Добавление нового рабочего потока решает проблему.

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

Пул потоков .NET имеет возможность вводить потоки каждый раз при завершении выполнения рабочего элемента или с интервалом в 500 миллисекунд, в зависимости от того, какой из них короче.Пул потоков использует эту возможность, чтобы попробовать добавить потоки (или убрать их), руководствуясь отзывами о предыдущих изменениях в количестве потоков.Если кажется, что добавление потоков повышает пропускную способность, пул потоков добавляет больше;в противном случае это уменьшает количество рабочих потоков.Этот метод называется эвристикой восхождения на холм.Следовательно, одна из причин сокращать выполнение отдельных задач - это избежать “обнаружения нехватки ресурсов", но другая причина сокращать их выполнение - это предоставить пулу потоков больше возможностей для повышения пропускной способности за счет регулировки количества потоков.Чем короче продолжительность отдельных задач, тем чаще пул потоков может измерять пропускную способность и соответствующим образом корректировать количество потоков.

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

Что не так с 500 рабочими потоками?В принципе, ничего, если у вас есть 500 ядер для их использования и огромные объемы системной памяти.Фактически, это долгосрочное видение параллельных вычислений.Однако, если на вашем компьютере не так много ядер, вы находитесь в ситуации, когда многие потоки конкурируют за временные интервалы.Это ситуация называется перегрузкой процессора.Позволяя множеству потоков с интенсивным использованием процессора конкурировать за время на одном ядре, увеличивается накладные расходы на переключение контекста, которые могут серьезно снизить общую пропускную способность системы .Даже если у вас не заканчивается память, производительность в этой ситуации может быть намного, намного хуже, чем при последовательных вычислениях.(Каждое переключение контекста занимает от 6000 до 8000 процессорных циклов.) Стоимость переключения контекста - не единственный источник накладных расходов.Управляемый поток в .NET потребляет примерно мегабайт стекового пространства независимо от того, используется это пространство для текущих выполняемых функций или нет.Требуется около 200 000 циклов процессора для создания нового потока и около 100 000 циклов для завершения работы потока.Это дорогостоящие операции.

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

Первый вариант - разбить ваше приложение на более короткие задачи, которые выполняются достаточно быстро для успешного выполнения пулом потоков контролируйте количество потоков для оптимальной пропускной способности.Вторая возможность - реализовать свой собственный планировщик задач объект, который не выполняет внедрение потока.Если ваши задачи имеют большую продолжительность, вам не нужен высокооптимизированный планировщик задач, потому что стоимость планирования будет незначительной по сравнению с временем выполнения задачи.Программа разработчика MSDN® содержит пример простой реализации планировщика задач, которая ограничивает максимальную степень параллелизма.Для получения дополнительной информации смотрите раздел “Дальнейшее чтение” в конце этой главы.

В качестве последнего средства вы можете использовать метод SetMaxThreads, чтобы настроить класс ThreadPool с верхним пределом количества рабочих потоков, обычно равным количеству ядер (это Окружающая среда.Свойство ProcessorCount).Это верхнее ограничение применяется для всего процесса, включая все домены приложений. "

Другие советы

Короткий ответ - нет.

Внутренне, TPL использует стандарт ThreadPool запланировать свои задачи. Так что вы на самом деле спрашиваете, ThreadPool принимает во внимание нагрузку машины, и это не так. Единственное, что ограничивает количество задач одновременно, - это количество потоков в пуле резьбы, ничего больше.

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

Выполнил тест, используя TPL / ThreadPool, чтобы запланировать множество задач, выполняющих петли спинов. Использование внешнего приложения, я загрузил один из сердечков на 100% с использованием сродства в процессе. Количество активных задач никогда не уменьшилось.

Даже лучше, я бегал несколько экземпляров одного и того же приложения с поддержкой CPU интенсивно .NET TPL. Количество потоков для всех приложений было одинаковым, и никогда не походил ниже количества ядер, даже если моя машина была едва пригодна.

Так что теория в сторону, TPL использует количество доступных ядер, но никогда не проверяет их фактическую нагрузку. Очень плохая реализация на мой взгляд.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top