0Pricing
Python Academy · Урок

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 — локальная установка не требуется.

Все уроки этого курса

  1. Потоки и GIL
  2. ThreadPoolExecutor
  3. Основы multiprocessing
  4. Безопасный обмен данными
← Назад к Python Academy