Uma co-rotina é uma função geradora que pode produzir valores e aceitar valores externos. O benefício de usar uma co-rotina é que podemos pausar a execução de uma função e retomá-la mais tarde. No caso de uma operação de rede, faz sentido pausar a execução de uma função enquanto esperamos pela resposta. Podemos usar o tempo para executar algumas outras funções.
Um futuro é como os Promise
objetos de Javascript. É como um espaço reservado para um valor que se materializará no futuro. No caso mencionado acima, enquanto espera pela E / S da rede, uma função pode nos dar um contêiner, uma promessa de que preencherá o contêiner com o valor quando a operação for concluída. Nós nos agarramos ao objeto futuro e quando ele for cumprido, podemos chamar um método nele para recuperar o resultado real.
Resposta direta: Você não precisa, ensure_future
se não precisa dos resultados. Eles são bons se você precisar dos resultados ou recuperar exceções ocorridas.
Créditos extras: eu escolheria run_in_executor
e aprovaria uma Executor
instância para controlar o número máximo de trabalhadores.
Explicações e códigos de amostra
No primeiro exemplo, você está usando corrotinas. A wait
função pega um monte de co-rotinas e as combina. Então wait()
termina quando todas as co-rotinas estiverem esgotadas (concluído / terminado retornando todos os valores).
loop = get_event_loop() #
loop.run_until_complete(wait(coros))
O run_until_complete
método garantiria que o loop estivesse ativo até que a execução fosse concluída. Observe como você não está obtendo os resultados da execução assíncrona neste caso.
No segundo exemplo, você está usando a ensure_future
função para envolver uma co-rotina e retornar um Task
objeto que é uma espécie de Future
. A corrotina está programada para ser executada no loop de evento principal quando você chama ensure_future
. O objeto futuro / tarefa retornado ainda não tem um valor, mas com o tempo, quando as operações de rede terminarem, o objeto futuro conterá o resultado da operação.
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))
Portanto, neste exemplo, estamos fazendo a mesma coisa, exceto que estamos usando futuros em vez de apenas usar corrotinas.
Vejamos um exemplo de como usar asyncio / corroutines / 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()
Aqui, usamos o create_task
método no loop
objeto. ensure_future
iria agendar a tarefa no loop de evento principal. Este método nos permite agendar uma co-rotina em um loop que escolhermos.
Também vemos o conceito de adicionar um retorno de chamada usando o add_done_callback
método no objeto de tarefa.
A Task
é done
quando a co-rotina retorna um valor, levanta uma exceção ou é cancelada. Existem métodos para verificar esses incidentes.
Escrevi algumas postagens de blog sobre esses tópicos que podem ajudar:
Claro, você pode encontrar mais detalhes no manual oficial: https://docs.python.org/3/library/asyncio.html
ensure_future()
? E se eu precisar do resultado, não posso simplesmente usarrun_until_complete(gather(coros))
?ensure_future
agenda a co-rotina a ser executada no loop de eventos. Então, eu diria que sim, é obrigatório. Mas é claro que você pode agendar as corrotinas usando outras funções / métodos também. Sim, você pode usargather()
- mas o collect irá esperar até que todas as respostas sejam coletadas.gather
ewait
realmente agrupar as corrotinas fornecidas como tarefas usandoensure_future
(veja as fontes aqui e aqui ). Portanto, não adianta usar deensure_future
antemão e não tem nada a ver com obter os resultados ou não.ensure_future
tem umloop
argumento, então não há razão para usarloop.create_task
overensure_future
. Erun_in_executor
não funcionará com corrotinas, um semáforo deve ser usado em seu lugar.create_task
overensure_future
, veja os documentos . Citaçãocreate_task() (added in Python 3.7) is the preferable way for spawning new tasks.
Resposta simples
async def
) NÃO a executa. Ele retorna objetos de co-rotina, como a função de gerador retorna objetos de gerador.await
recupera valores de co-rotinas, ou seja, "chama" a co-rotinaeusure_future/create_task
agende a co-rotina para executar no loop de eventos na próxima iteração (embora não espere que eles terminem, como um encadeamento daemon).Alguns exemplos de código
Vamos primeiro esclarecer alguns termos:
async def
está;Caso 1,
await
em uma co-rotinaCriamos duas corrotinas,
await
uma e usamoscreate_task
para executar a outra.você obterá o resultado:
Explicar:
task1 foi executado diretamente e task2 foi executado na iteração seguinte.
Caso 2, cedendo controle ao loop de eventos
Se substituirmos a função principal, podemos ver um resultado diferente:
você obterá o resultado:
Explicar:
Ao chamar
asyncio.sleep(1)
, o controle foi devolvido ao loop de eventos e o loop verifica se há tarefas a serem executadas e, em seguida, executa a tarefa criada porcreate_task
.Observe que primeiro invocamos a função de co-rotina, mas não
await
ela, então apenas criamos uma única co-rotina, e não a executamos. Então, chamamos a função de corrotina novamente e a envolvemos em umacreate_task
chamada, creat_task irá realmente agendar a corrotina para rodar na próxima iteração. Portanto, no resultado,create task
é executado antesawait
.Na verdade, o objetivo aqui é devolver o controle ao loop, você poderia usar
asyncio.sleep(0)
para ver o mesmo resultado.Sob o capô
loop.create_task
realmente chamaasyncio.tasks.Task()
, que chamaráloop.call_soon
. Eloop.call_soon
colocará a tarefa emloop._ready
. Durante cada iteração do loop, ele verifica cada retorno de chamada em loop._ready e o executa.asyncio.wait
asyncio.ensure_future
e ,asyncio.gather
na verdade, ligarloop.create_task
direta ou indiretamente.Observe também na documentação :
fonte
await task2
ligação poderia ser esclarecido. Em ambos os exemplos, a chamada loop.create_task () é o que agenda task2 no loop de evento. Portanto, em ambos os exs você pode excluir oawait task2
e ainda o task2 eventualmente será executado. No ex2 o comportamento será idêntico, poisawait task2
acredito que esteja apenas agendando a tarefa já concluída (que não será executada uma segunda vez), enquanto no ex1 o comportamento será um pouco diferente, pois a tarefa2 não será executada até que o main seja concluído. Para ver a diferença, adicioneprint("end of main")
no final do ex1 principalUm comentário de Vincent com link para https://github.com/python/asyncio/blob/master/asyncio/tasks.py#L346 , que mostra que
wait()
envolve as corrotinasensure_future()
para você!Em outras palavras, precisamos de um futuro, e as corrotinas serão silenciosamente transformadas nelas.
Atualizarei essa resposta quando encontrar uma explicação definitiva de como agrupar co-rotinas / futuros.
fonte
c
,await c
é equivalente aawait create_task(c)
?Da BDFL [2013]
Tarefas
Com isso em mente,
ensure_future
faz sentido como um nome para a criação de uma Tarefa, pois o resultado do Future será computado independentemente de você o esperar ou não (desde que você espere algo). Isso permite que o loop de eventos conclua sua tarefa enquanto você espera por outras coisas. Observe que no Python 3.7create_task
é a forma preferida de garantir um futuro .Nota: Eu mudei "rendimento de" nos slides de Guido para "aguardar" aqui pela modernidade.
fonte