concurrent.futures — Запуск параллельных задач
Новое в версии 3.2.
Исходный код: Lib/concurrent/futures/thread.py и Lib/concurrent/futures/process.py
Модуль concurrent.futures предоставляет высокоуровневый интерфейс для асинхронного выполнения вызываемых объектов.
Асинхронное выполнение может быть реализовано с использованием потоков, с помощью ThreadPoolExecutor, или отдельных процессов, с помощью ProcessPoolExecutor. Оба реализуют один и тот же интерфейс, определяемый абстрактным классом Executor.
1. Объекты Executor
-
class concurrent.futures.Executor -
Абстрактный класс, предоставляющий методы для асинхронного выполнения вызовов. Он не должен использоваться напрямую, но только через свои конкретные подклассы.
-
submit(fn, *args, **kwargs) -
Планирует вызываемый объект, fn, для выполнения как
fn(*args **kwargs)и возвращает объектFuture, представляющий выполнение вызываемого объекта.with ThreadPoolExecutor(max_workers=1) as executor: future = executor.submit(pow, 323, 1235) print(future.result())
-
map(func, *iterables, timeout=None, chunksize=1) -
Эквивалентно
map(func, *iterables), за исключением того, что func выполняется асинхронно, и несколько вызовов func могут быть выполнены одновременно. Возвращаемый итератор генерирует исключениеconcurrent.futures.TimeoutError, если к нему применяется метод__next__(), и результат не доступен через timeout секунд с момента первоначального вызоваExecutor.map(). timeout может быть целым числом или числом с плавающей точкой. Если timeout не указан илиNone, время ожидания не ограничено. Если вызов вызывает исключение, это исключение будет поднято при получении его значения из итератора. При использованииProcessPoolExecutor, этот метод разбивает iterables на несколько частей, которые он отправляет в пул в виде отдельных задач. Размер (приблизительный) этих частей можно указать, установив chunksize в положительное целое число. Для очень длинных итерируемых объектов использование большого значения для chunksize может значительно улучшить производительность по сравнению с размером по умолчанию 1. СThreadPoolExecutor, chunksize не влияет на результат.Изменено в версии 3.5: Добавлен аргумент chunksize.
-
shutdown(wait=True) -
Посылает сигнал исполнителю, что он должен освободить все используемые ресурсы, когда текущие ожидающие задачи закончат свое выполнение. Вызовы
Executor.submit()иExecutor.map()после остановки будут вызыватьRuntimeError.Если wait
True, то этот метод не вернётся, пока все ожидающие задачи не завершат выполнение, и ресурсы, связанные с исполнителем, не будут освобождены. Если waitFalse, то этот метод вернётся немедленно, и ресурсы, связанные с исполнителем, будут освобождены, когда все ожидающие задачи закончат выполнение. Независимо от значения wait, вся программа Python не завершит работу, пока все ожидающие задачи не завершат выполнение.Вы можете избежать явного вызова этого метода, если используете оператор
with, который выключитExecutor(ожидая, как будтоExecutor.shutdown()был вызван со значением wait, равнымTrue):import shutil with ThreadPoolExecutor(max_workers=4) as e: e.submit(shutil.copy, 'src1.txt', 'dest1.txt') e.submit(shutil.copy, 'src2.txt', 'dest2.txt') e.submit(shutil.copy, 'src3.txt', 'dest3.txt') e.submit(shutil.copy, 'src4.txt', 'dest4.txt')
-
2. ThreadPoolExecutor
ThreadPoolExecutor — это подкласс Executor, который использует пул потоков для асинхронного выполнения вызовов.
Тупики могут возникнуть, когда вызываемый объект, связанный с Future, ждёт результатов другого Future. Например:
import time
def wait_on_b():
time.sleep(5)
print(b.result()) # b will never complete because it is waiting on a.
return 5
def wait_on_a():
time.sleep(5)
print(a.result()) # a will never complete because it is waiting on b.
return 6
executor = ThreadPoolExecutor(max_workers=2)
a = executor.submit(wait_on_b)
b = executor.submit(wait_on_a)
И:
def wait_on_future():
f = executor.submit(pow, 5, 2)
# This will never complete because there is only one worker thread and
# it is executing this function.
print(f.result())
executor = ThreadPoolExecutor(max_workers=1)
executor.submit(wait_on_future)
-
class concurrent.futures.ThreadPoolExecutor(max_workers=None) -
Подкласс
Executor, который использует пул не более max_workers потоков для асинхронного выполнения вызовов.Изменено в версии 3.5: Если max_workers равно
Noneили не задано, по умолчанию оно будет равно количеству процессоров на машине, умноженному на5, предполагая, чтоThreadPoolExecutorчасто используется для перекрытия операций ввода/вывода, а не работы ЦП, и количество потоков должно быть выше, чем количество потоков дляProcessPoolExecutor.
2.1. Пример ThreadPoolExecutor
import concurrent.futures
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']
# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
with urllib.request.urlopen(url, timeout=timeout) as conn:
return conn.read()
# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
# Start the load operations and mark each future with its URL
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
print('%r generated an exception: %s' % (url, exc))
else:
print('%r page is %d bytes' % (url, len(data)))
3. ProcessPoolExecutor
Класс ProcessPoolExecutor — это подкласс Executor, который использует пул процессов для асинхронного выполнения вызовов. ProcessPoolExecutor использует модуль multiprocessing, что позволяет ему обойти Глобальную блокировку интерпретатора, но также означает, что выполняться и возвращаться могут только сериализуемые объекты.
Модуль __main__ должен быть импортируем подпроцессами-рабочими. Это означает, что ProcessPoolExecutor не будет работать в интерактивном интерпретаторе.
Вызов методов Executor или Future из вызываемого объекта, переданного в ProcessPoolExecutor, приведёт к тупику.
-
class concurrent.futures.ProcessPoolExecutor(max_workers=None) -
Подкласс
Executor, выполняющий вызовы асинхронно с использованием пула не более max_workers процессов. Если max_workers равноNoneили не задано, по умолчанию оно будет равно количеству процессоров на машине. Если max_workers меньше или равно0, будет поднято исключениеValueError.Изменено в версии 3.3: Когда один из рабочих процессов завершается внезапно, теперь возникает ошибка
BrokenProcessPool. Раньше поведение было неопределённым, но операции с исполнителем или его задачами часто замораживались или приводили к тупику.
3.1. Пример ProcessPoolExecutor
import concurrent.futures
import math
PRIMES = [
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419]
def is_prime(n):
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
print('%d is prime: %s' % (number, prime))
if __name__ == '__main__':
main()
4. Объекты Future
Класс Future инкапсулирует асинхронное выполнение вызываемого объекта. Экземпляры Future создаются методом Executor.submit().
-
class concurrent.futures.Future -
Инкапсулирует асинхронное выполнение вызываемого объекта.
Futureэкземпляры создаются с помощьюExecutor.submit()и не должны создаваться напрямую, кроме как для тестирования.-
cancel() -
Попытка отменить вызов. Если вызов выполняется в данный момент и не может быть отменён, метод вернёт
False, иначе вызов будет отменён, и метод вернётTrue.
-
cancelled() -
Возвращает
Trueесли вызов был успешно отменён.
-
running() -
Возвращает
Trueесли вызов выполняется в данный момент и не может быть отменён.
-
done() -
Возвращает
Trueесли вызов был успешно отменён или завершён.
-
result(timeout=None) -
Возвращает значение, возвращённое вызовом. Если вызов ещё не завершён, этот метод будет ждать до timeout секунд. Если вызов не завершится в течение timeout секунд, будет поднято исключение
concurrent.futures.TimeoutError. timeout может быть целым или дробным числом. Если timeout не указан илиNone, время ожидания не ограничено.Если задача отменена до завершения, будет поднято исключение
CancelledError.Если вызов поднял исключение, этот метод поднимет то же исключение.
-
exception(timeout=None) -
Возвращает исключение, поднятое вызовом. Если вызов ещё не завершён, этот метод будет ждать до timeout секунд. Если вызов не завершится в течение timeout секунд, будет поднято исключение
concurrent.futures.TimeoutError. timeout может быть целым или дробным числом. Если timeout не указан илиNone, время ожидания не ограничено.Если задача отменена до завершения, будет поднято исключение
CancelledError.Если вызов завершился без подъёма исключения, возвращается
None.
-
add_done_callback(fn) -
Прикрепляет вызываемый объект fn к задаче. fn будет вызван, с задачей в качестве единственного аргумента, когда задача отменена или завершена.
Добавленные вызываемые объекты вызываются в порядке их добавления и всегда вызываются в потоке, принадлежащем процессу, который их добавил. Если вызываемый объект поднимает исключение
Exceptionподкласс, он будет записан в журнал и проигнорирован. Если вызываемый объект поднимает исключениеBaseExceptionподкласс, поведение не определено.Если задача уже завершена или отменена, fn будет вызван немедленно.
Следующие методы
Futureпредназначены для использования в модульных тестах и реализацияхExecutor.-
set_running_or_notify_cancel() -
Этот метод должен вызываться только реализациями
Executorперед выполнением работы, связанной сFuture, и модульными тестами.Если метод возвращает
False, тоFutureбыла отменена, т.е. был вызванFuture.cancel()и возвращеноTrue. Любые потоки, ожидающие завершенияFuture(т.е. черезas_completed()илиwait()) будут разбужены.Если метод возвращает
True, тоFutureне была отменена и переведена в состояние выполнения, т.е. вызовыFuture.running()вернутTrue.Этот метод может быть вызван только один раз и не может быть вызван после того, как были вызваны
Future.set_result()илиFuture.set_exception().
-
5. Модульные функции
-
concurrent.futures.wait(fs, timeout=None, return_when=ALL_COMPLETED) -
Ожидание завершения экземпляров
Future(возможно, созданных различными экземплярамиExecutor) с помощью fs. Возвращает именованную пару множеств. Первое множество, названноеdone, содержит задачи, которые завершились (закончились или были отменены) до завершения ожидания. Второе множество, названноеnot_done, содержит незавершенные задачи.timeout может быть использован для управления максимальным временем ожидания в секундах до возврата. timeout может быть целым или дробным числом. Если timeout не указан или
None, время ожидания не ограничено.return_when указывает, когда функция должна вернуть результат. Он должен быть одним из следующих констант:
Константа Описание FIRST_COMPLETEDФункция вернёт результат, когда любая задача завершится или будет отменена. FIRST_EXCEPTIONФункция вернёт результат, когда любая задача завершится, подняв исключение. Если ни одна задача не поднимет исключение, это эквивалентно ALL_COMPLETED.ALL_COMPLETEDФункция вернёт результат, когда все задачи завершатся или будут отменены.
-
concurrent.futures.as_completed(fs, timeout=None) -
Возвращает итератор по экземплярам
Future(возможно, созданным различными экземплярамиExecutor), передаваемыми в fs, который возвращает задачи по мере их завершения (завершения или отмены). Любые дублируемые задачи, переданные в fs, будут возвращены один раз. Любые задачи, завершившиеся до вызоваas_completed(), будут возвращены в первую очередь. Возвращаемый итератор подниметconcurrent.futures.TimeoutError, если метод__next__()будет вызван, и результат не будет доступен в течение timeout секунд с момента первоначального вызоваas_completed(). timeout может быть целым или дробным числом. Если timeout не указан илиNone, время ожидания не ограничено.
См. также
- PEP 3148 – futures - асинхронное выполнение вычислений
- Предложение, описывающее эту функцию для включения в стандартную библиотеку Python.
6. Классы исключений
-
exception concurrent.futures.CancelledError -
Поднимается, когда задача отменяется.
-
exception concurrent.futures.TimeoutError -
Вызывается, когда операция с будущим превышает заданный таймаут.
-
exception concurrent.futures.process.BrokenProcessPool -
Производный от
RuntimeError, этот класс исключений вызывается, когда один из рабочих процессовProcessPoolExecutorзавершился некорректно (например, если он был убит извне).New in version 3.3.
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/concurrent.futures.html