Пул потоков
Оригинальное название: thread pool
Коротко
Набор заранее запущенных рабочих потоков, которые по очереди берут задачи из общей очереди. Потоки не создаются и не уничтожаются под каждую задачу.
Что такое «Пул потоков»
Создать поток — операция не бесплатная: системе нужно выделить память под стек, зарегистрировать исполнителя в планировщике. Если на каждую мелкую задачу запускать новый поток, накладные расходы съедят выигрыш от параллельности, а при всплеске нагрузки потоков станет столько, что процессор будет больше переключаться между ними, чем работать. Пул решает обе проблемы: исполнителей фиксированное число, они живут долго, а поступающая работа ждёт своей очереди.
Устройство пула
- При старте пул создаёт N рабочих потоков.
- Задачи складываются в очередь.
- Свободный поток берёт задачу, выполняет её и возвращается за следующей.
- Если очередь пуста, потоки ждут, не нагружая процессор.
Готовые реализации есть почти везде: ExecutorService в Java, ThreadPoolExecutor в Python, пул потоков в .NET. В Node.js JavaScript выполняется в одном потоке, но файловые операции и часть криптографии уходят во внутренний пул библиотеки libuv — по умолчанию в нём четыре потока, размер меняют переменной UV_THREADPOOL_SIZE.
Пример: проверка ссылок на Python
from concurrent.futures import ThreadPoolExecutor
import urllib.request
def check(url):
with urllib.request.urlopen(url, timeout=10) as r:
return url, r.status
with ThreadPoolExecutor(max_workers=8) as pool:
for url, status in pool.map(check, urls):
print(url, status)
Сотня адресов проверяется восемью потоками: пока одни ждут ответа сети, другие уже отправляют следующие запросы. Обработку ошибок сети в реальном скрипте стоит добавить внутрь check.
Как выбрать размер
Для задач, которые нагружают процессор — сжатие, расчёты, — разумный размер близок к числу ядер: больше потоков не ускорит работу. Для задач, которые в основном ждут сети или диска, потоков можно сделать больше, но тогда ограничением становятся внешний сервис и база данных. Тот же принцип в мире PHP работает на уровне процессов: PHP-FPM держит пул рабочих процессов, и параметр pm.max_children задаёт их предел. Слишком маленький пул — запросы стоят в очереди и сайт отвечает медленно, слишком большой — серверу не хватает памяти.

