Solución:
Para responder a sus preguntas, proporcionaré 3 soluciones diferentes al mismo problema.
caso 1: solo pitón normal
import time
def sleep():
print(f'Time: {time.time() - start:.2f}')
time.sleep(1)
def sum(name, numbers):
total = 0
for number in numbers:
print(f'Task {name}: Computing {total}+{number}')
sleep()
total += number
print(f'Task {name}: Sum = {total}n')
start = time.time()
tasks = [
sum("A", [1, 2]),
sum("B", [1, 2, 3]),
]
end = time.time()
print(f'Time: {end-start:.2f} sec')
producción:
Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3
Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6
Time: 5.02 sec
caso 2: async / await hecho mal
import asyncio
import time
async def sleep():
print(f'Time: {time.time() - start:.2f}')
time.sleep(1)
async def sum(name, numbers):
total = 0
for number in numbers:
print(f'Task {name}: Computing {total}+{number}')
await sleep()
total += number
print(f'Task {name}: Sum = {total}n')
start = time.time()
loop = asyncio.get_event_loop()
tasks = [
loop.create_task(sum("A", [1, 2])),
loop.create_task(sum("B", [1, 2, 3])),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
end = time.time()
print(f'Time: {end-start:.2f} sec')
producción:
Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3
Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6
Time: 5.01 sec
caso 3: async / await hecho bien (igual que en el caso 2, excepto que sleep
función)
import asyncio
import time
async def sleep():
print(f'Time: {time.time() - start:.2f}')
await asyncio.sleep(1)
async def sum(name, numbers):
total = 0
for number in numbers:
print(f'Task {name}: Computing {total}+{number}')
await sleep()
total += number
print(f'Task {name}: Sum = {total}n')
start = time.time()
loop = asyncio.get_event_loop()
tasks = [
loop.create_task(sum("A", [1, 2])),
loop.create_task(sum("B", [1, 2, 3])),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
end = time.time()
print(f'Time: {end-start:.2f} sec')
producción:
Task A: Computing 0+1
Time: 0.00
Task B: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task B: Computing 1+2
Time: 1.00
Task A: Sum = 3
Task B: Computing 3+3
Time: 2.00
Task B: Sum = 6
Time: 3.01 sec
case 1
con case 2
dar lo mismo 5 seconds
, mientras que case 3
solo 3 seconds
. Entonces el async/await done right
es más rápido.
La razón de la diferencia está en la implementación de sleep
función.
# case 1
def sleep():
print(f'Time: {time.time() - start:.2f}')
time.sleep(1)
# case 2
async def sleep():
print(f'Time: {time.time() - start:.2f}')
time.sleep(1)
# case 3
async def sleep():
print(f'Time: {time.time() - start:.2f}')
await asyncio.sleep(1)
sleep
funcionar en case 1
y case 2
son lo mismo”. Ellos “duermen” sin permitir que otros usen los recursos. Mientras que case 3
permite el acceso a los recursos cuando está dormido.
En case 2
agregamos async
a la función normal. Sin embargo, el bucle de eventos lo ejecutará sin interrupción. ¿Por qué? Porque no dijimos dónde se le permite al bucle interrumpir su función para ejecutar otra tarea.
En case 3
le dijimos al bucle de eventos exactamente dónde interrumpir la función para ejecutar otra tarea. ¿Donde exactamente?
# case 3
async def sleep():
print(f'Time: {time.time() - start:.2f}')
await asyncio.sleep(1) # <-- Right here!
Más sobre esto lea aquí
Actualización 02 / mayo / 2020
Considere leer
- Una guía para autostopistas sobre programación asincrónica
- Asyncio Futures y Coroutines
¿Es posible dar un ejemplo sencillo que muestre cómo
async
/await
funciona, usando solo estas dos palabras clave +asyncio.get_event_loop()
+
run_until_complete
+ otro código Python pero ningún otroasyncio
funciones?
De esta forma es posible escribir código que funcione:
import asyncio
async def main():
print('done!')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Pero de esta manera es imposible demostrar por qué necesitas asyncio.
Por cierto, ¿por qué necesitas? asyncio
, no solo código simple? La respuesta es – asyncio
le permite obtener beneficios de rendimiento cuando paraleliza las operaciones de bloqueo de E / S (como leer / escribir en la red). Y para escribir un ejemplo útil, debe usar la implementación asíncrona de esas operaciones.
Lea esta respuesta para obtener una explicación más detallada.
Upd:
ok, aquí tienes un ejemplo que usa asyncio.sleep
para imitar la operación de bloqueo de E / S y asyncio.gather
que muestra cómo puede ejecutar múltiples operaciones de bloqueo al mismo tiempo:
import asyncio
async def io_related(name):
print(f'{name} started')
await asyncio.sleep(1)
print(f'{name} finished')
async def main():
await asyncio.gather(
io_related('first'),
io_related('second'),
) # 1s + 1s = over 1s
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Producción:
first started
second started
first finished
second finished
[Finished in 1.2s]
Note como ambos io_related
Comenzó entonces, después de solo un segundo, ambos terminaron.
Python 3.7+ ahora tiene una API más simple (en mi opinión) con una redacción más simple (más fácil de recordar que “asegurar_futuro”): puede usar create_task
que devuelve un objeto Task (que puede ser útil más adelante para cancelar la tarea si es necesario).
Ejemplo básico 1
import asyncio
async def hello(i):
print(f"hello {i} started")
await asyncio.sleep(4)
print(f"hello {i} done")
async def main():
task1 = asyncio.create_task(hello(1)) # returns immediately, the task is created
await asyncio.sleep(3)
task2 = asyncio.create_task(hello(2))
await task1
await task2
asyncio.run(main()) # main loop
Resultado:
hola 1 comencé
hola 2 comenzó
hola 1 hecho
hola 2 hecho
Ejemplo básico 2
Si necesita obtener el valor de retorno de estas funciones asincrónicas, entonces gather
es útil. El siguiente ejemplo está inspirado en la documentación, pero desafortunadamente el documento no muestra lo que gather
es realmente útil para: obtener los valores de retorno.
import asyncio
async def factorial(n):
f = 1
for i in range(2, n + 1):
print(f"Computing factorial({n}), currently i={i}...")
await asyncio.sleep(1)
f *= i
return f
async def main():
L = await asyncio.gather(factorial(2), factorial(3), factorial(4))
print(L) # [2, 6, 24]
asyncio.run(main())
Rendimiento esperado:
Calculando factorial (2), actualmente i = 2 …
Calculando factorial (3), actualmente i = 2 …
Calculando factorial (4), actualmente i = 2 …
Calculando factorial (3), actualmente i = 3 …
Calculando factorial (4), actualmente i = 3 …
Calculando factorial (4), actualmente i = 4 …
[2, 6, 24]
PD: incluso si usa asyncio
, y no trio
, el tutorial de este último me ayudó a asimilar la programación asincrónica de Python.