Explicación detallada de la IO asincrónica de rutina de Python (asyncio)

1. Introducción a las corrutinas

1.1 Definición

Las corrutinas no son subprocesos a nivel de sistema. Las corrutinas a menudo se denominan "hilos ligeros", "microhilos", "fibras", etc. En pocas palabras, las corrutinas pueden considerarse como funciones diferentes en un hilo, y estas funciones pueden alternar rápidamente entre sí.

Las corrutinas están muy cerca de los subprocesos en modo de usuario. El cambio entre subprocesos en modo de usuario no necesita quedar atrapado en el kernel. Sin embargo, el cambio entre subprocesos en modo de usuario en algunos sistemas operativos requiere la ayuda de subprocesos en modo kernel.

Las corrutinas son características proporcionadas por los lenguajes de programación (o libs) (los programadores pueden determinar el método y el proceso de cambio entre corrutinas) y son operaciones en modo de usuario. Las corrutinas son adecuadas para tareas intensivas en IO. Los lenguajes comunes que brindan soporte nativo para rutinas incluyen: c ++ 20, golang, python, etc. Otros lenguajes brindan funciones de rutina en forma de bibliotecas, como Tencent's Fiber y libco antes de C ++ 20, etc.

1.2 Clasificación 

Hay dos tipos de corrutinas, una sin pila , representada por asyncio en python , y otra con pila , representada por gevent en python Este artículo explica principalmente los subprocesos asyncio.

Hilo con pila

Hilo sin pila

Observación

ejemplo:

hilo lua

ventilación de pitón

retorno de rendimiento de C#

C# asíncrono\espera

asincio de Python

ninguno

Ya sea para tener un contexto separado:

No El contexto incluye registros, marcos de pila.

Ubicación de almacenamiento de variables locales:

pila montón Las variables locales de las corrutinas sin pila se almacenan en el montón, como los miembros de datos del generador.

ventaja:

1. Cada rutina tiene un contexto separado y esta rutina se puede suspender en cualquier lugar de cualquier función anidada.

2. No se requiere ningún compilador para admitir la sintaxis; se puede implementar mediante instrucciones de ensamblaje.

1. No es necesario guardar un contexto separado para cada rutina y el uso de memoria es bajo.

2. Bajo costo de conmutación y mayor rendimiento.

ninguno

defecto:

1. Es necesario asignar un cierto tamaño de memoria de montón por adelantado para guardar cada contexto de rutina, por lo que se producirá un desperdicio de memoria o un desbordamiento de la pila.

2. El costo de copiar y cambiar de contexto es alto y el rendimiento es menor que el de las rutinas sin pila.

1. El compilador debe proporcionar soporte semántico, como el azúcar de sintaxis de retorno de rendimiento de C#.

2. Esta rutina solo se puede suspender dentro de este generador y no se puede suspender en funciones anidadas.

3. Las palabras clave son contagiosas hasta cierto punto y el código asincrónico debe tener las palabras clave correspondientes. Por el contrario, las corrutinas apiladas solo necesitan realizar las llamadas a funciones correspondientes.

Una rutina sin pila no puede suspender esta rutina en una función anidada. Dado que la rutina apilada se implementa guardando y cambiando contextos, incluidos registros y pilas de ejecución, la rutina se puede generar y despertar dentro de la función anidada de la función de rutina.

2. Explicación detallada de la rutina asíncio de Python

2.1 Introducción

asyncio es una biblioteca para escribir código simultáneo usando la sintaxis async/await.

asyncio se utiliza como base para múltiples marcos asincrónicos que proporcionan Python de alto rendimiento, incluidos servicios de red y sitios web, bibliotecas de conexión de bases de datos, colas de tareas distribuidas y más.

asyncio suele ser la mejor opción para crear código de red altamente estructurado y con uso intensivo de IO.

asyncio proporciona un conjunto de API de alto nivel para:

  • Ejecute corrutinas de Python simultáneamente y obtenga control total sobre su ejecución;
  • Realizar red IO e IPC;
  • Controlar los procesos secundarios;
  • Implementar tareas distribuidas a través de colas;
  • Sincronizar código concurrente;
  • Cree y administre bucles de eventos para proporcionar API asincrónicas para redes, ejecutar procesos secundarios, manejar señales del sistema operativo y más;
  • Utilice transportes para implementar protocolos eficientes;
  • Conecte bibliotecas y código basados ​​en devolución de llamadas con sintaxis async/await.

2.2 Uso de rutina asíncio (usando la sintaxis python3.8)

Dirección del código fuente de la función asyncio: https://github.com/python/cpython/tree/3.8/Lib/asyncio

1) Las corrutinas se declaran mediante la sintaxis async/await , que es la forma recomendada de escribir aplicaciones asyncio.

La función asyncio.run() se utiliza para ejecutar la función "main()" del punto de entrada de nivel superior.

La función asyncio.sleep(delay, result=None, *, loop=None) se utiliza para bloquear durante el número de segundos especificado.

# coding=utf8

import sys
import asyncio

async def main():
     print('hello')
     await asyncio.sleep(1)
     print('world')

asyncio.run(main())

2) Función de bucle de eventos (incluida la creación, ejecución y parada del bucle)

La función asyncio.get_running_loop() devuelve el bucle de eventos en ejecución en el hilo actual del sistema operativo.
La función asyncio.get_event_loop() obtiene el bucle de eventos actual.
La función asyncio.set_event_loop(loop) establece el bucle en el bucle de eventos actual del subproceso actual del sistema operativo.
La función asyncio.new_event_loop() crea un nuevo bucle de eventos.
La función loop.run_until_complete(future) se ejecuta hasta que se completa el futuro (una instancia de Future).
La función loop.run_forever() ejecuta el bucle de eventos hasta que se llama a stop().
La función loop.stop() detiene el bucle de eventos.
La función loop.is_running() devuelve True si el bucle de eventos se está ejecutando actualmente.
La función loop.is_closed() devuelve True si el bucle de eventos se ha cerrado.
La función loop.close() cierra el bucle de eventos.
La función loop.create_future() crea un objeto asyncio.Future adjunto al bucle de eventos.
La función loop.create_task(coro, *, name=None) programa la ejecución de una corrutina. Devuelve un objeto Tarea.
La función loop.set_task_factory(factory) establece una fábrica de tareas, utilizada en loop.create_task().
La función loop.get_task_factory() devuelve una fábrica de tareas, o Ninguna si se utiliza el valor predeterminado.

 Ejemplo 1:

# coding=utf8

import sys
import asyncio

async def fun1():
    await asyncio.sleep(1)
    print('协程1')

async def fun2():
    await asyncio.sleep(1)
    print('协程2')

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait([fun1(), fun2()]))
loop.close()

Ejemplo 2:

# coding=utf8

import sys
import asyncio
import time

# 一个对future进行赋值的函数
async def slow_operation(future, num):
    await asyncio.sleep(1)
    # 给future赋值
    future.set_result('Future'+ str(num) +' is done!')

def main():
    loop = asyncio.get_event_loop()
    # 创建一个future
    future1 = loop.create_future()
    # 使用ensure_future 创建Task
    asyncio.ensure_future(slow_operation(future1, 1))

    future2 = loop.create_future()
    asyncio.ensure_future(slow_operation(future2, 2))

    # gather Tasks,并通过run_uniti_complete来启动、终止loop
    loop.run_until_complete(asyncio.gather(future1, future2))

    print(future1.result())
    print(future2.result())

    loop.close()

if __name__ == "__main__":
    main()
    

3) Programación de devoluciones de llamadas y devoluciones de llamadas retrasadas 

La función loop.call_soon(callback, *args, context=None) organiza la llamada a la devolución de llamada con el argumento args en la siguiente iteración del bucle de eventos. Las devoluciones de llamada se realizan en el orden en que fueron registradas. Cada devolución de llamada se llama solo una vez. El método no es seguro para subprocesos.
La función loop.call_soon_threadsafe(callback, *args, context=None) es una variante segura para subprocesos de call_soon(). Debe usarse para programar devoluciones de llamadas desde otros hilos.
La función loop.call_later(delay, callback, *args, context=None) organiza la llamada a la devolución de llamada después de un retraso determinado en segundos (puede ser int o float).
La función loop.call_at(when, callback, *args, context=None) programa la devolución de llamada para que se llame en la marca de tiempo absoluta dada (un int o float), utilizando la misma referencia de tiempo que loop.time().
La función loop.time() devuelve la hora actual como un valor flotante basado en el reloj monótono dentro del bucle de tiempo.

# coding=utf8

import sys
import asyncio
from threading import Thread
import time

def callback(arg, loop):
    print('回调函数arg={} 回调的时间time={}'.format(arg, loop.time()))

async def task(loop):
    now = loop.time()
    print('时钟时间:{}'.format(time.time()))
    print('时事件循环时间:{}'.format(loop.time()))
    print('注册回调函数')
    loop.call_at(now + 1, callback, 'call_at1', loop) # 等待1秒执行 call_at 函数
    loop.call_at(now + 2, callback, 'call_at2', loop)
    loop.call_later(3, callback, 'call_later1', loop) # 等待3秒执行 call_later 函数
    loop.call_later(4, callback, 'call_later2', loop)
    loop.call_soon(callback, 'call_soon', loop) # 立即执行执行 call_soon 函数
    await asyncio.sleep(4)

def main():
    event_loop = asyncio.get_event_loop()
    try:
        print('进入事件循环监听')
        event_loop.run_until_complete(task(event_loop))  # 将事件循环对象传入task函数中
    finally:
        print('关闭事件循环监听')
        event_loop.close()

if __name__ == "__main__":
    main()

4) Conexión de socket y función Streams

  • loop.create_connection(protocol_factory, host=Ninguno, puerto=Ninguno, *, ssl=Ninguno, familia=0, proto=0, flags=0, sock=Ninguno, local_addr=Ninguno, server_hostname=Ninguno, ssl_handshake_timeout=Ninguno, happy_eyeballs_delay= Ninguno, interleave=Ninguno) abre una conexión de transmisión a la dirección especificada por el host y el puerto.
  • loop.create_server(protocol_factory, host=Ninguno, puerto=Ninguno, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=Ninguno, backlog=100, ssl=Ninguno, reuse_address=Ninguno, reuse_port=Ninguno, ssl_handshake_timeout= Ninguno, start_serving=True) La función crea un servicio TCP (tipo de socket SOCK_STREAM) que escucha el puerto de la dirección del host.
  • La función loop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, start_serving=True) es similar a loop.create_server() pero es específica de la familia de sockets AF_UNIX . ruta es el nombre del socket de dominio Unix requerido, a menos que se proporcione el parámetro sock. Se admiten sockets Unix abstractos, str, bytes y Path.
  • La función loop.connect_accepted_socket(protocol_factory, sock, *, ssl=None, ssl_handshake_timeout=None) envuelve la conexión aceptada en un par de transporte/protocolo.
  • La función loop.sock_recv(sock, nbytes) recibe hasta nbytes de sock. Versión asincrónica de socket.recv().
  • La función loop.sock_recv_into(sock, buf) recibe datos de sock y los coloca en el búfer buf. Imita el método de bloqueo socket.recv_into().
  • La función loop.sock_sendall(sock, data) envía datos al socket del calcetín. Versión asincrónica de socket.sendall().
  • La función loop.sock_accept(sock) acepta una conexión. Imita el método de bloqueo socket.accept().
  • La función loop.sock_sendfile(sock, file, offset=0, count=None, *, fallback=True) envía archivos utilizando el sistema os.sendfile de alto rendimiento cuando sea posible. Devuelve el número total de bytes enviados.
  • asyncio.open_connection(host=Ninguno, puerto=Ninguno, *, bucle=Ninguno, límite=Ninguno, ssl=Ninguno, familia=0, proto=0, banderas=0, sock=Ninguno, local_addr=Ninguno, server_hostname=Ninguno, La función ssl_handshake_timeout=None) establece una conexión de red y devuelve un par de objetos (lector, escritor).

  • asyncio.start_server(client_connected_cb, host=Ninguno, puerto=Ninguno, *, loop=Ninguno, límite=Ninguno, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=Ninguno, backlog=100, ssl=Ninguno, reuse_address= Ninguno, reuse_port=Ninguno, ssl_handshake_timeout=Ninguno, start_serving=True) la función inicia el servicio de socket.

  • La función asyncio.open_unix_connection(path=Ninguno, *, loop=Ninguno, limit=Ninguno, ssl=Ninguno, sock=Ninguno, server_hostname=Ninguno, ssl_handshake_timeout=Ninguno) establece una conexión de socket Unix y devuelve (lector, escritor) esto al valor de retorno. Similar a open_connection(), pero opera en sockets Unix.

  • La función asyncio.start_unix_server(client_connected_cb, path=None, *, loop=None, limit=None, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, start_serving=True) inicia un servicio de socket Unix . Similar a start_server(), pero opera en un socket Unix.

  • La clase asyncio.StreamReader representa un objeto lector que proporciona una API para leer datos de una secuencia IO.

    La función lector.read(n=-1) lee n bytes. Si n no está configurado, se establece automáticamente en -1, lee en EOF y devuelve todos los bytes leídos.
    La función lectora.readline() lee una línea, donde "línea" se refiere a una secuencia de bytes que termina en \n. Si se lee EOF y no se encuentra \n, este método devuelve los datos leídos parcialmente. Si se lee EOF y el búfer interno está vacío, se devuelve un objeto de bytes vacío.
    La función lector.readexactly(n) lee n bytes con precisión, ni más ni menos.
    lector.readuntil(separator=b'\n') La función lee datos de la secuencia hasta que se encuentra un separador. Después del éxito, los datos y el separador especificado se eliminarán (o consumirán) del búfer interno. Los datos devueltos incluirán el separador especificado al final. Si la cantidad de datos leídos excede el límite de flujo configurado, se generará una excepción LimitOverrunError y los datos permanecerán en el búfer interno y se podrán leer nuevamente. Si se alcanza EOF antes de encontrar un separador completo, se genera una excepción IncompleteReadError y se restablece el búfer interno. La propiedad IncompleteReadError.partial puede contener parte del separador especificado.
    La función lectora.at_eof() devuelve True si el búfer está vacío y se llama a feed_eof().

  • La clase asyncio.StreamWriter representa un objeto de escritura que proporciona una API para escribir datos en una secuencia de IO.

    La función escritor.write(data) intentará escribir datos en el socket subyacente inmediatamente. Si la escritura falla, los datos se ponen en cola en el búfer de escritura interno hasta que se puedan enviar.
    La función escritor.writelines(data) intenta inmediatamente escribir una lista de cadenas de bytes (o cualquier objeto iterable) en el socket subyacente. Si la escritura falla, los datos se ponen en cola en el búfer de escritura interno hasta que se puedan enviar.
    La función escritor.close() cierra la secuencia y el socket subyacente.
    La función escritor.can_write_eof() devuelve True si el transporte subyacente admite el método write_eof(); de lo contrario, devuelve False.
    La función escritor.write_eof() cierra el final de escritura de la secuencia después de que se hayan vaciado los datos de escritura almacenados en el búfer.
    La función escritor.transport() devuelve el transporte asincio subyacente.
    La función escritor.drain() espera hasta que se pueda reanudar adecuadamente la escritura en la secuencia.
    La función escritor.is_closing() devuelve True si la secuencia se ha cerrado o se está cerrando.
    La función escritor.wait_closed() espera hasta que se cierre la transmisión.

código del servidor: 

# coding=utf8

import asyncio
from asyncio import StreamReader, StreamWriter


async def echo(reader: StreamReader, writer: StreamWriter):
    data = await reader.read(1024)
    message = data.decode()
    addr = writer.get_extra_info('peername')

    print(f"Received {message} from {addr}")
    print(f"Send: {message}")

    writer.write(data)
    await writer.drain()

    writer.close()


async def main(host, port):
    server = await asyncio.start_server(echo, host, port)
    addr = server.sockets[0].getsockname()
    print(f'Serving on {addr}')
    async with server:
        await server.serve_forever()

asyncio.run(main("127.0.0.1", 9999))

codigo del cliente:

# coding=utf8

import asyncio


async def tcp_echo_client(message):
    reader, writer = await asyncio.open_connection('127.0.0.1', 9999)
    print(f'Send to server: {message}')

    writer.write(message.encode())
    await writer.drain()

    data = await reader.read(1024)
    print(f'Received from server: {data.decode()}')

    writer.close()
    await writer.wait_closed()


if __name__ == '__main__':
    while True:
        send_msg = input("send: ")
        asyncio.run(tcp_echo_client(send_msg))

5) Ejecutar código en un subproceso o grupo de procesos

La función loop.run_in_executor(executor, func, *args) organiza la llamada a func en el ejecutor especificado.

# coding=utf8

import asyncio
import concurrent.futures

def blocking_io():
    # File operations (such as logging) can block the
    # event loop: run them in a thread pool.
    with open('/dev/urandom', 'rb') as f:
        return f.read(100)

def cpu_bound():
    # CPU-bound operations will block the event loop:
    # in general it is preferable to run them in a
    # process pool.
    return sum(i * i for i in range(5))

async def main():
    loop = asyncio.get_running_loop()

    ## Options:

    # 1. Run in the default loop's executor:
    result = await loop.run_in_executor(
        None, blocking_io)
    print('default thread pool', result)
    print("\n")

    # 2. Run in a custom thread pool:
    with concurrent.futures.ThreadPoolExecutor() as pool:
        result = await loop.run_in_executor(
            pool, blocking_io)
        print('custom thread pool', result)
        print("\n")

    # 3. Run in a custom process pool:
    with concurrent.futures.ProcessPoolExecutor() as pool:
        result = await loop.run_in_executor(
            pool, cpu_bound)
        print('custom process pool', result)

asyncio.run(main())



6) La función asyncio.create_task(coro, *, name=None) se utiliza para empaquetar una corrutina en una Tarea, programarla para su ejecución y devolver el objeto Tarea.

# coding=utf8

import sys
import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

    print(f"started at {time.strftime('%X')}")

    # Wait until both tasks are completed (should take around 2 seconds.)
    await task1
    await task2

    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

7) API de manejo de errores

La función loop.set_exception_handler(handler) establece el controlador como el nuevo controlador de excepciones del bucle de eventos.
La función loop.get_exception_handler() devuelve el controlador de excepciones actual, o Ninguno si no se establece ningún controlador de excepciones.
loop.default_exception_handler(context) controlador de excepciones predeterminado de la función.
La función loop.call_exception_handler(context) llama al controlador de excepciones del bucle de eventos actual.
La función loop.get_debug() obtiene la configuración del modo de depuración del bucle de eventos (bool).
La función loop.set_debug(enabled: bool) establece el modo de depuración del bucle de eventos.

# coding=utf8

import sys
import asyncio

def handle_exception(loop, context): 
    print('Error:', context['message']) 
 
async def my_task(): 
    await asyncio.sleep(1)
    print('task1')
 
loop = asyncio.get_event_loop() 
loop.set_exception_handler(handle_exception) 
loop.run_until_complete(my_task()) 
loop.close()

8)Futuro 

La función asyncio.Future(*, loop=None) es un futuro que representa el resultado final de una operación asincrónica. No es seguro para subprocesos.
La función asyncio.isfuture(obj) se utiliza para determinar si obj es un ejemplo de la clase asyncio.Future, una instancia de la clase asyncio.Task o un objeto con el atributo _asyncio_future_blocking y devuelve True.
La función asyncio.ensure_future(obj, *, loop=None) crea una nueva tarea.
La función asyncio.wrap_future(future, *, loop=None) encapsula un objeto concurrent.futures.Future en un objeto asyncio.Future.

Funciones futuras relacionadas con objetos:

La función fut.result() devuelve el resultado de un Futuro.
La función fut.set_result(resultado) marca el Futuro como completo y establece el resultado.
La función fut.set_exception(exception) marca el Futuro como completo y establece una excepción.
La función fut.done() devuelve True si se completa el Futuro.
La función fut.cancelled() devuelve True si el Futuro ha sido cancelado.
La función fut.add_done_callback(callback, *, context=None) agrega una función de devolución de llamada que se ejecuta cuando se completa el Futuro.
La función fut.remove_done_callback(callback) elimina la devolución de llamada de la lista de devolución de llamada.
La función fut.cancel() cancela el Futuro y envía la función de devolución de llamada.
La función fut.exception() devuelve la excepción establecida de Future.
La función fut.get_loop() devuelve el bucle de eventos al que se ha vinculado el objeto Future.

# coding=utf8

import sys
import asyncio
import time

# 定义一个协程
async def slow_operation(fut):
    await asyncio.sleep(1)
    fut.set_result(22)

def def_callback(fut):
    number = fut.result()
    print(number + 1)

def main():
    # 获得全局循环事件
    loop = asyncio.get_event_loop()

    # 实例化期物对象
    fut = asyncio.Future()
    asyncio.ensure_future(slow_operation(fut))

    # 执行回调函数
    fut.add_done_callback(def_callback)

    # loop 的 run_until_complete 会将 _run_until_complete_cb 添加到 future 的完成回调列表中。而 _run_until_complete_cb 中会执行 loop.stop() 方法
    loop.run_until_complete(fut)

    # 关闭事件循环对象
    loop.close()

if __name__ == "__main__":
    main()

9) La función asyncio.gather(*aws, loop=None, return_exceptions=False) se utiliza para ejecutar simultáneamente objetos que se pueden esperar en la secuencia de aws. Si un objeto que se puede esperar en AWS es una rutina, se agregará automáticamente a la programación como una tarea.

# coding=utf8

import sys
import asyncio

async def factorial(name, number):
    f = 1
    for i in range(2, number + 1):
        print(f"Task {name}: Compute factorial({i})...")
        await asyncio.sleep(1)
        f *= i
    print(f"Task {name}: factorial({number}) = {f}")

async def main():
    # Schedule three calls *concurrently*:
    await asyncio.gather(
        factorial("A", 2),
        factorial("B", 3),
        factorial("C", 4),
    )

asyncio.run(main())

10) La función asyncio.shield(aw, *, loop=None) se utiliza para proteger un objeto en espera para que no se cancele.

# coding=utf8

import sys
import asyncio

async def task_func(number):
    await asyncio.sleep(1)
    print('函数执行成功:'+str(number))
 
async def cancel_task(task):
    await asyncio.sleep(0.2)
    was_cancelled = task.cancel()
    print(f'cancelled: {was_cancelled}')
 
async def main():
    coro = task_func(1)
    task = asyncio.create_task(coro)
    shielded = asyncio.shield(task)
    asyncio.create_task(cancel_task(shielded))
    try:
        result = await shielded
        print(f'>got: {result}')
    except asyncio.CancelledError:
        print('shielded was cancelled')

    await asyncio.sleep(1)
    print(f'shielded: {shielded}')
    print(f'task: {task}')
 
asyncio.run(main())

11) La función asyncio.wait(aws, *, loop=None, timeout=None, return_when=ALL_COMPLETED) ejecuta simultáneamente los objetos que se pueden esperar en el objeto iterable de aws y entra en el estado de bloqueo hasta que se cumplan las condiciones especificadas por return_when.

return_when especifica cuándo debe regresar esta función. Debe ser una de las siguientes constantes:

constante

describir

FIRST_COMPLETED

La función volverá cuando finalice o cancele cualquier objeto en espera.

FIRST_EXCEPTION

La función regresará cuando cualquier objeto en espera finalice lanzando una excepción. Es equivalente cuando no se lanza ninguna excepción  ALL_COMPLETED.

ALL_COMPLETED

La función volverá cuando todos los objetos en espera hayan finalizado o hayan sido cancelados.

# coding=utf8

import sys
import asyncio

async def coroutine_example(name):
    print('正在执行name:', name)
    await asyncio.sleep(1)
    print('执行完毕name:', name)

loop = asyncio.get_event_loop()

tasks = [coroutine_example('Zarten_' + str(i)) for i in range(3)]
wait_coro = asyncio.wait(tasks)
loop.run_until_complete(wait_coro)
loop.close()

12) La función asyncio.wait_for(aw, timeout, *, loop=None) espera a que se complete el objeto de espera aw y expira después de los segundos de tiempo de espera especificados.

# coding=utf8

import sys
import asyncio

async def eternity():
    # Sleep for one hour
    await asyncio.sleep(3600)
    print('yay!')

async def main():
    # Wait for at most 1 second
    try:
        await asyncio.wait_for(eternity(), timeout=1)
    except asyncio.TimeoutError:
        print('timeout!')

asyncio.run(main())

13) La función asyncio.run_coroutine_threadsafe(coro, loop) envía una corrutina al bucle de eventos especificado. A salvo de amenazas.

# coding=utf8

import sys
import asyncio
import threading

async def main(i): 
    while True:
        await asyncio.sleep(1)
        print(i)
 
async def production_task():
    for i in range(1, 4):
        # 将不同参数main这个协程循环注册到运行在线程中的循环,
        # thread_loop会获得一循环任务
        asyncio.run_coroutine_threadsafe(main(i),thread_loop)
        # 注意:run_coroutine_threadsafe 这个方法只能用在运行在线程中的循环事件使用
 
def start_loop(thread_loop):
     #  运行事件循环, loop以参数的形式传递进来运行
    asyncio.set_event_loop(thread_loop)
    thread_loop.run_forever()
 
if __name__ == '__main__':
    
    # 获取一个事件循环
    thread_loop = asyncio.new_event_loop()
    # 将次事件循环运行在一个线程中,防止阻塞当前主线程,运行线程,同时协程事件循环也会运行
    threading.Thread(target=start_loop, args=(thread_loop,)).start()
    
    # 将生产任务的协程注册到这个循环中
    loop = asyncio.get_event_loop()
    # 运行次循环
    loop.run_until_complete(production_task())

14) La función asyncio.current_task(loop=None) devuelve la instancia de Tarea actualmente en ejecución, o Ninguna si no hay ninguna tarea en ejecución. Si el bucle es Ninguno, se utilizará get_running_loop() para obtener el bucle de eventos actual.

La función asyncio.all_tasks(loop=None) devuelve una colección de objetos Task sin terminar ejecutados por el bucle de eventos. Si el bucle es Ninguno, se utilizará get_running_loop() para obtener el bucle de eventos actual.

# coding=utf8

import sys
import asyncio
import time

async def my_coroutine():
    task = asyncio.current_task()
    print(task)

async def main():
    task1 = asyncio.create_task(my_coroutine())
    task2 = asyncio.create_task(my_coroutine())
    tasks = [task1, task2]
    await asyncio.gather(*tasks)

asyncio.run(main())

15) La función asyncio.Task(coro, *, loop=None, name=None) es un objeto similar a Future, que puede ejecutar rutinas de Python. No es seguro para subprocesos. 

Funciones relacionadas con el objeto de tarea:

La función task.cancelled() devuelve True si se cancela el objeto Task.
La función task.done() devuelve True si el objeto Task se ha completado.
La función task.result() devuelve el resultado de la tarea.
La función task.exception() devuelve la excepción del objeto Task.
La función task.remove_done_callback(callback) elimina la devolución de llamada de la lista de devolución de llamada.
La función task.get_stack(*, limit=None) devuelve la lista del marco de pila de este objeto Task.
La función task.print_stack(*, limit=None, file=None) imprime la pila o el rastreo de este objeto Task.
La función task.get_coro() devuelve el objeto de rutina envuelto por Task.
La función task.get_name() devuelve el nombre de la tarea.
La función task.set_name(value) establece el nombre de la tarea.

# coding=utf8

import sys
import asyncio
import time

async def cancel_me():
    print('cancel_me(): before sleep')

    try:
        # Wait for 1 hour
        await asyncio.sleep(3600)
    except asyncio.CancelledError:
        print('cancel_me(): cancel sleep')
        raise
    finally:
        print('cancel_me(): after sleep')

async def main():
    # Create a "cancel_me" Task
    task = asyncio.create_task(cancel_me())

    # Wait for 1 second
    await asyncio.sleep(1)

    task.cancel()
    try:
        await task
    except asyncio.CancelledError:
        print("main(): cancel_me is cancelled now")

asyncio.run(main())

16) Corrutinas basadas en generadores (en desuso después de Python 3.10)

Las corrutinas basadas en generadores son las predecesoras de la sintaxis async/await. Son generadores de Python creados utilizando el rendimiento de declaraciones que pueden esperar a futuros y otras corrutinas.

@asyncio.coroutine Decorador utilizado para marcar corrutinas basadas en generadores.

La función asyncio.iscoroutine(obj) devuelve True si obj es un objeto de rutina.

La función asyncio.iscoroutinefunction(func) devuelve True si func es una función de rutina.

# coding=utf8

import sys
import asyncio
import time

@asyncio.coroutine # 标志协程的装饰器
def taskIO_1():
    print('开始运行IO任务1...')
    yield from asyncio.sleep(2)  # 假设该任务耗时2s
    print('IO任务1已完成,耗时2s')
    return taskIO_1.__name__

@asyncio.coroutine # 标志协程的装饰器
def taskIO_2():
    print('开始运行IO任务2...')
    yield from asyncio.sleep(3)  # 假设该任务耗时3s
    print('IO任务2已完成,耗时3s')
    return taskIO_2.__name__

asyncio.run(taskIO_1())
asyncio.run(taskIO_2())

Supongo que te gusta

Origin blog.csdn.net/m0_68949064/article/details/132805165
Recomendado
Clasificación