ThreadPoolExecutor
Запускайте задачи параллельно
«ThreadPoolExecutor» — бесплатный урок Python Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Python Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Python Academy содержит 4 уроков всего.
Зачем нужен пул потоков
Создавать потоки и вручную дожидаться их завершения утомительно. concurrent.futures.ThreadPoolExecutor управляет пулом рабочих потоков за Вас и удобно возвращает результаты.
from concurrent.futures import ThreadPoolExecutor
def square(n):
return n * n
with ThreadPoolExecutor() as ex:
future = ex.submit(square, 5)
print(future.result())submit и будущий результат
submit() планирует вызов и немедленно возвращает объект, представляющий будущий результат. Вызовите у него .result(), чтобы получить значение; выполнение будет приостановлено до готовности результата.
from concurrent.futures import ThreadPoolExecutor
def greet(name):
return 'hi ' + name
with ThreadPoolExecutor(max_workers=2) as ex:
f1 = ex.submit(greet, 'Ana')
f2 = ex.submit(greet, 'Bob')
print(f1.result())
print(f2.result())map для множества входных данных
executor.map(func, iterable) одновременно применяет функцию к каждому элементу и выдаёт результаты в порядке входных данных.
from concurrent.futures import ThreadPoolExecutor
def cube(n):
return n ** 3
with ThreadPoolExecutor() as ex:
results = ex.map(cube, range(5))
print(list(results))Управление количеством рабочих потоков
max_workers ограничивает число потоков, работающих одновременно. Для задач, связанных с вводом-выводом, рабочих потоков может быть больше, чем ядер процессора.
from concurrent.futures import ThreadPoolExecutor
import time
def io_task(n):
time.sleep(0.05)
return n
start = time.perf_counter()
with ThreadPoolExecutor(max_workers=4) as ex:
print(list(ex.map(io_task, range(4))))
print('ran concurrently')as_completed
as_completed(futures) выдаёт каждый будущий результат сразу после его готовности, независимо от порядка отправки задач. Это удобно для обработки результатов сразу после их получения.
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
def work(n):
time.sleep(0.01 * (3 - n))
return n
with ThreadPoolExecutor() as ex:
futures = [ex.submit(work, i) for i in range(3)]
for f in as_completed(futures):
print('finished', f.result())Обработка исключений
Если задача вызывает исключение, оно сохраняется в связанном с ней объекте результата и повторно возникает при вызове .result(). Оберните этот вызов в конструкцию try/except.
from concurrent.futures import ThreadPoolExecutor
def risky(n):
if n == 0:
raise ValueError('cannot be zero')
return 10 // n
with ThreadPoolExecutor() as ex:
f = ex.submit(risky, 0)
try:
print(f.result())
except ValueError as e:
print('caught:', e)Передача нескольких аргументов в map
map принимает несколько итерируемых объектов и объединяет их элементы в аргументы, как встроенная функция map.
from concurrent.futures import ThreadPoolExecutor
def add(a, b):
return a + b
with ThreadPoolExecutor() as ex:
print(list(ex.map(add, [1, 2, 3], [10, 20, 30])))Сбор результатов в словарь
Распространённый приём — сопоставить каждый объект результата с его входными данными, чтобы при использовании as_completed понимать, какому входу соответствует результат.
from concurrent.futures import ThreadPoolExecutor, as_completed
def length(word):
return len(word)
words = ['cat', 'tiger', 'ox']
with ThreadPoolExecutor() as ex:
future_to_word = {ex.submit(length, w): w for w in words}
out = {}
for f in as_completed(future_to_word):
out[future_to_word[f]] = f.result()
print(sorted(out.items()))Завершение работы менеджером контекста
Использование исполнителя в блоке with автоматически вызывает shutdown() и дожидается завершения всех ожидающих задач.
from concurrent.futures import ThreadPoolExecutor
def job(n):
return n * 2
with ThreadPoolExecutor() as ex:
futures = [ex.submit(job, i) for i in range(3)]
print('all tasks done after the with block')
print([f.result() for f in futures])Проверка состояния результата
Объект будущего результата предоставляет методы .done() и .running(), поэтому можно проверять ход выполнения, не блокируя программу.
from concurrent.futures import ThreadPoolExecutor
def quick(n):
return n + 1
with ThreadPoolExecutor() as ex:
f = ex.submit(quick, 41)
result = f.result()
print('done?', f.done())
print('value:', result)Когда это использовать
ThreadPoolExecutor идеально подходит для множества небольших задач, связанных с вводом-выводом: HTTP-вызовов, чтения файлов и запросов к базе данных. Для вычислительных задач используйте ProcessPoolExecutor.
from concurrent.futures import ThreadPoolExecutor
urls = ['a', 'b', 'c']
def fetch(u):
return 'fetched ' + u
with ThreadPoolExecutor(max_workers=3) as ex:
for r in ex.map(fetch, urls):
print(r)Быстрая проверка
Проверьте, насколько хорошо Вы поняли ThreadPoolExecutor.
Итоги
Вы изучили ThreadPoolExecutor:
submit()возвращает объект будущего результата;map()выполняет функцию по итерируемому объекту в исходном порядке.as_completed()выдаёт объекты будущих результатов по мере их завершения.- Исключения становятся видны через
result(). - Блок
withуправляет завершением работы.
Далее: настоящий параллелизм с помощью многопроцессной обработки.
Часто задаваемые вопросы
Урок «ThreadPoolExecutor» бесплатный?
Да — полный текст урока «ThreadPoolExecutor» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Python Academy, подпишись на CoddyKit PRO. Курс Python Academy содержит 4 уроков всего.
Чему я научусь в уроке «ThreadPoolExecutor»?
Запускайте задачи параллельно Ты практикуешь Python Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Python Academy?
Предыдущий опыт не требуется. Python Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «ThreadPoolExecutor»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Python Academy?
Да. Каждый урок Python Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Потоки и GIL
- ThreadPoolExecutor
- Основы multiprocessing
- Безопасный обмен данными