Когда использовать TaskCreationOptions.LongRunning?

Я давно это задавался, но так и не нашел ответа.

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

То, что я не знаю (и на удивление не могу найти нигде в Интернете), - это какое-то "эмпирическое правило", когда нужно указать задачу как долговременную. Это одна секунда? 30 секунд? Минута? 5 минут? Имеет ли отношение к количеству задач, которые использует приложение? Должен ли я, как программист делать некоторые вычисления С#threads в пуле потоков, сколько задач я создаю, сколько будет долго работать в одно и то же время, и на основе этого принять решение о том, использовать ли долговременную задачу?

Надеюсь узнать что-то здесь.

Ответы

Ответ 1

Он может быть определен количественно, диспетчер threadpool добавляет дополнительный поток за пределы оптимального, когда существующие потоки tp не завершатся достаточно быстро. Он делает это два раза в секунду, до максимума, заданного SetMaxThreads(). Который имеет очень высокое значение по умолчанию. Оптимальным является количество процессорных ядер, доступных для машины, 4 типично. Выполнение большего количества потоков, чем доступные ядра, может быть вредным из-за перераспределения ресурсов контекста.

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

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

В противном случае вам будет необходимо убедиться, что предположение, что диспетчер threadpool сделает, является точным. Задача должна занять много времени, потому что она не использует процессор эффективно. Диспетчер задач - это простой способ увидеть, что делают ядра процессора в вашей программе, хотя он не скажет вам, какой именно код они выполняют. Вам понадобится unit test, чтобы увидеть выполняемый поток изолированно. Конечным и единственным полностью точным способом сказать вам, что использование LongRunning было правильным выбором, - проверить, действительно ли ваше приложение выполняет больше работы.

Ответ 2

Изменение ответа Ганса, с которым я в основном согласен.

Самая важная причина, указывающая LongRunning, - получить практически гарантированное и почти немедленное выполнение. Ожидается, что пул потоков не выведет поток в ваш рабочий элемент. Я говорю "практически", потому что ОС свободно не планировать ваш поток. Но вы получите некоторую долю процессора, и обычно это не займет много времени, пока это не произойдет.

Вы прыгаете перед очередью, указав LongRunning. Не нужно ждать, пока пул потоков будет выдавать 2 потока в секунду, если он находится под нагрузкой.

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

Запуск и остановка расхода потока составляет порядка 1 мс времени процессора. Это намного больше, чем выпуск рабочих элементов пула потоков. Я просто оценил это как 3M, выпущенных и завершенных в секунду. Тест был довольно искусственным, но порядок был прав.

LongRunning задокументирован как намек, но он абсолютно эффективен на практике. Нет эвристики, которая учитывает ваш намек. Он считается правильным.

Ответ 3

когда указать задачу как долговременную

Это зависит от того, что делает задача. Если задача содержит while(true) {...} и живет до выключения приложения, тогда имеет смысл указать LongRunning. Если вы создаете задачу для очереди некоторой операции и предотвращаете блокировку текущего потока, тогда вам все равно (ничего не указывать).

Это зависит от других задач. Не имеет значения, выполняете ли вы несколько задач с помощью или без LongRunning. Но может возникнуть проблема для создания тысяч задач, каждая из которых требует нового потока. Или наоборот, вы можете испытать нить голодания, не указав его.

Один простой способ подумать об этом: вы предпочитаете, чтобы новая задача запускалась в новом потоке, или вам все равно? Если сначала - используйте LongRunningOption. Этот не означает, какая задача будет выполняться в другом потоке, это просто хороший критерий, когда вы должны указать его.

например. при использовании ContinueWith, тогда LongRunning противоположно ExecuteSynchronously (есть check, чтобы оба параметра не указывались). Если у вас есть несколько продолжений, то, возможно, вы хотите избежать накладных расходов на очередь и запустить определенное продолжение в том же потоке или наоборот - вы не хотите, чтобы одно из продолжений вмешивалось в другие, а затем вы можете специально использовать LongRunning. См. в этой статьеthis), чтобы узнать около ExecuteSynchronously.

Ответ 4

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

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

Подход к нему для переключения всех операций ввода-вывода (файл, сеть, БД и т.д.) на асинхронный.

Также существуют длительные задачи из-за длительных вычислений с интенсивным вычислением.

Подход состоит в том, чтобы отложить вычисления, например, вставить await Task.Yield() в определенные моменты или, предпочтительно, рассчитать вычисление явно путем планирования одной задачи за другой, каждый из которых обрабатывает ранее разделенный блок данных или обрабатывает буфер до ограниченного времени предел.

"Слишком много времени" зависит от вас.

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

например. в ASP.NET в IIS, посмотрите, какое среднее время, затраченное на запрос для наиболее распространенных запросов. Аналогичным образом, в службе, в которой используется пул потоков, например, для обработки очереди сообщений принимайте среднее значение для каждого сообщения.

В более общем плане, "слишком много времени", когда рабочие элементы ставятся в очередь быстрее, чем обрабатываются. Там могут быть всплески работы, поэтому вам следует усреднить это за единицу времени, которая имеет для вас значение, будь то секунда, минута, 10 минут и т.д. Когда у вас есть SLA, вы должны определить этот интервал где-то.

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


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