Разница между coroutine и future / task в Python 3.5?
Допустим у нас есть функция манекена:
async def foo(arg):
result = await some_remote_call(arg)
return result.upper()
в чем разница между:
coros = []
for i in range(5):
coros.append(foo(i))
loop = get_event_loop()
loop.run_until_complete(wait(coros))
и:
from asyncio import ensure_future
futures = []
for i in range(5):
futures.append(ensure_future(foo(i)))
loop = get_event_loop()
loop.run_until_complete(wait(futures))
Примечание: пример возвращает результат, но это не фокус вопроса. Если возвращаемое значение имеет значение, используйте gather()
вместо wait()
.
независимо от возвращаемого значения, я ищу ясность на ensure_future()
. wait(coros)
и wait(futures)
оба запускают сопрограммы, поэтому когда и почему должен быть сопрограмма завернутый в ensure_future
?
в принципе, как правильно (tm) запускать кучу неблокирующих операций с использованием Python 3.5 async
?
для дополнительного кредита, что делать, если я хочу, чтобы пакет вызовов? Например, мне нужно позвонить some_remote_call(...)
1000 раз, но я не хочу давить на базе Web-сервера//и т. д. с 1000 одновременных подключений. Это выполнимо с потоком или пулом процессов, но есть ли способ сделать это с asyncio
?
3 ответа:
сопрограмма-это функция генератора, которая может как выдавать значения, так и принимать значения извне. Преимущество использования сопрограммы заключается в том, что мы можем приостановить выполнение функции и возобновить его позже. В случае сетевой операции имеет смысл приостановить выполнение функции, пока мы ждем ответа. Мы можем использовать это время для выполнения некоторых других функций.
будущее похоже на
Promise
объекты из Javascript. Это как держатель места для значения это будет материализовано в будущем. В вышеупомянутом случае, ожидая сетевого ввода-вывода, функция может дать нам контейнер, обещание, что он заполнит контейнер значением, когда операция завершится. Мы держимся за будущий объект, и когда он будет выполнен, мы можем вызвать метод на нем, чтобы получить фактический результат.Прямого Ответа: не нужно
ensure_future
если вам не нужны результаты. Они хороши, если вам нужны результаты или получения случались исключения.Дополнительные Кредиты: я бы выбрал
run_in_executor
и передатьExecutor
экземпляр для управления количеством максимальных работников.пояснения и примеры кодов
в первом примере вы используете сопрограммы. Элемент
wait
функция принимает кучу сопрограмм и объединяет их вместе. Так чтоwait()
заканчивается, когда все сопрограммы исчерпаны (завершено/закончено возвращение всех значений).loop = get_event_loop() # loop.run_until_complete(wait(coros))
The
run_until_complete
метод будет убедиться, что цикл жив, пока выполнение не будет завершено. Обратите внимание, как вы не получаете результаты асинхронного выполнения в этом случае.во втором примере вы используете
ensure_future
функция для обертывания сопрограммы и возвратаTask
объект, который является своего родаFuture
. Сопрограмму планируется выполнить в основном цикле событий при вызовеensure_future
. Возвращенный объект future / task не имеет еще есть значение, но со временем, когда сетевые операции закончатся, будущий объект будет содержать результат операции.from asyncio import ensure_future futures = [] for i in range(5): futures.append(ensure_future(foo(i))) loop = get_event_loop() loop.run_until_complete(wait(futures))
давайте посмотрим на пример о том, как использовать ввода-вывода/сопрограммы/фьючерсы:
import asyncio async def slow_operation(): await asyncio.sleep(1) return 'Future is done!' def got_result(future): print(future.result()) # We have result, so let's stop loop.stop() loop = asyncio.get_event_loop() task = loop.create_task(slow_operation()) task.add_done_callback(got_result) # We run forever loop.run_forever()
здесь мы использовали
create_task
метод
комментарий Винсента, связанный с https://github.com/python/asyncio/blob/master/asyncio/tasks.py#L346, что показывает, что
wait()
обертывает сопрограммы вensure_future()
для вас!я обновлю этот ответ, когда найду окончательное объяснение того, как пакетные сопрограммы/фьючерсы.
задачи
- это сопрограмма, завернутая в будущее
- class Task-это подкласс класса Future
- С ждут слишком!
- чем он отличается от голой сопрограммы?
- он может сделать прогресс, не дожидаясь его
- пока вы ждете чего-то другого, т. е.
- ждут [something_else]
имея это в виду,
ensure_future
имеет смысл как имя для создания задачи, так как результат будущего будет вычислен независимо от того, будете ли вы ждут это (пока вы чего-то ждете). Это позволяет циклу событий выполнить вашу задачу, пока вы ждете других вещей. Обратите внимание, что в Python 3.7create_task
является предпочтительным способом обеспечить a будущее.Примечание: я изменил "выход из" в слайдах Гвидо, чтобы "ждать" здесь для современности.