Подтвердить что ты не робот

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

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

Например:

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

Короткий ответ: no.

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

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

Ответ 3

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

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

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