Пул управляемых потоков

Класс System.Threading.ThreadPool обеспечивает приложение пулом рабочих потоков, управляемых системой, позволяя пользователю сосредоточиться на выполнении задач приложения, а не на управлении потоками. Если имеются небольшие задачи, которые требуют фоновой обработки, пул управляемых потоков — это самый простой способ воспользоваться преимуществами нескольких потоков. В Framework 4 и более поздних версиях использовать пул потоков стало значительно проще, так как вы можете создавать объекты Task и Task<TResult>, которые выполняют в потоках пула асинхронные задачи.

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

Характеристики пула потоков

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

Для каждого процесса существует только один пул потоков.

Исключения в потоках из пула потоков

Необработанные исключения в потоках из пула приводят к завершению процесса. Есть три исключения из этого правила:

  • Исключение System.Threading.ThreadAbortException возникает в потоке пула вследствие вызова Thread.Abort.
  • Исключение System.AppDomainUnloadedException возникает в потоке пула вследствие выгрузки домена приложения.
  • Среда CLR или процесс ведущего приложения прерывает выполнение потока.

См. дополнительные сведения об исключениях в управляемых потоках.

Максимальное число потоков в пуле потоков

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

Вы можете управлять максимальным количеством потоков с помощью методов ThreadPool.GetMaxThreads и ThreadPool.SetMaxThreads.

Примечание.

В коде, содержащем среду CLR, этот размер можно задать с помощью метода ICorThreadpool::CorSetMaxThreads.

Минимальные значения пула потоков

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

Примечание.

Если потребность низкая, фактическое количество потоков из пула потоков может быть ниже минимальных значений.

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

Внимание

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

Использование пула потоков

Самым простым способом использования пула потоков является применение библиотеки параллельных задач (TPL). По умолчанию такие типы TPL, как Task и Task<TResult>, используют потоки из пула для выполнения задач.

Пул потоков также можно использовать путем вызова ThreadPool.QueueUserWorkItem из управляемого кода (или ICorThreadpool::CorQueueUserWorkItem из неуправляемого кода) и передачи делегата System.Threading.WaitCallback, представляющего метод, который выполняет задачу.

Другим способом использования пула потоков является помещение в очередь рабочих элементов, которые имеют отношение к операции ожидания, с помощью метода ThreadPool.RegisterWaitForSingleObject и передача дескриптора System.Threading.WaitHandle, который вызывает метод, представленный делегатом System.Threading.WaitOrTimerCallback, при получении сигнала или истечении времени ожидания. Потоки из пула потоков используются для вызова методов обратного вызова.

Примеры см. по ссылкам на страницы API.

Пропуск проверок безопасности

Пул потоков также предоставляет методы ThreadPool.UnsafeQueueUserWorkItem и ThreadPool.UnsafeRegisterWaitForSingleObject. Используйте эти методы только в том случае, если вы уверены, что стек вызывающего объекта не важен для проверок безопасности, осуществляемых во время выполнения задачи в очереди. ThreadPool.QueueUserWorkItem и ThreadPool.RegisterWaitForSingleObject перехватывают стек вызывающего объекта, который объединяется со стеком потока из пула потоков, когда поток начинает выполнять задачу. Если требуется проверка безопасности, проверяется весь стек. Несмотря на обеспечение безопасности, такая проверка также влияет на производительность.

Когда не следует использовать потоки из пула потоков

Существует ряд сценариев, в которых следует создавать собственные потоки и работать с ними, а не использовать потоки из пула:

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

См. также