Python asyncio: Entrar en un contexto asincrónico temporal?

0x5453:

Quiero escribir una biblioteca que mezcla el trabajo síncrona y asíncrona, como:

def do_things():
    # 1) do sync things
    # 2) launch a bunch of slow async tasks and block until they are all complete or an exception is thrown
    # 3) more sync work
    # ...

Empecé a implementar esta usando asynciocomo excusa para aprender el aprender la biblioteca, pero a medida que aprendo más parece que este puede ser el enfoque equivocado. Mi problema es que no parece ser una forma limpia de hacer 2, ya que depende del contexto de la persona que llama. Por ejemplo:

  • No puedo utilizar asyncio.run(), debido a que la persona que llama ya podría tener un ciclo de eventos en ejecución y que sólo puede tener un bucle por hilo.
  • Marcado do_thingscomo asynces demasiado pesado, ya que no debería requerir la persona que llama para ser asíncrono. Además, si do_things era async , código de llamada síncrona ( 1y 3) de una asyncfunción parece ser una mala práctica .
  • asyncio.get_event_loop()También parece mal, porque puede crear un nuevo bucle, que si está funcionando izquierda evitaría que la persona que llama desde la creación de su propio bucle después de llamar do_things(aunque podría decirse que no deberían hacer eso). Y en base a la documentación de loop.close, parece que iniciar / detener los lazos múltiples en un solo hilo no va a funcionar.

Básicamente se parece como si quiero usar asyncioen absoluto, estoy obligado a utilizarlo para toda la vida del programa, y por lo tanto todas las bibliotecas como ésta tiene que ser escrito como sea sincrónica o asincrónica 100% 100%. Pero el comportamiento que quiero es: Utilice el bucle de eventos actual si se está ejecutando, de lo contrario crear una temporal sólo por el alcance de 2, y no romper el código del cliente al hacerlo. Hace algo como esto existe, o es asynciola decisión equivocada?

user4815162342:

No puedo usar asyncio.run (), debido a que la persona que llama ya podría tener un ciclo de eventos en ejecución y que sólo puede tener un bucle por hilo.

Si la persona que llama tiene un ciclo de eventos en ejecución, no se debe ejecutar el bloqueo de código en primer lugar porque va a bloquear el asa de la persona que llama!

Con esto en mente, su mejor opción es de hecho hacer que do_thingsel código asíncrono y sincronización llamada mediante run_in_executorel cual está diseñado precisamente por ese caso de uso:

async def do_things():
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, sync_stuff)
    await async_func()
    await loop.run_in_executor(None, more_sync_stuff)

Esta versión de do_thingsse puede utilizar desde el código asíncrono como await do_things()ya partir de código de sincronización como asyncio.run(do_things()).

Una vez dicho esto ... si usted sabe que el código de sincronización se ejecutará muy brevemente, o si es por alguna razón dispuesto a bloquear el bucle de eventos de la persona que llama, se puede evitar la limitación de comenzar un ciclo de eventos en un hilo separado:

def run_async(aw):
    result = None
    async def run_and_store_result():
        nonlocal result
        result = await aw
    t = threading.Thread(target=asyncio.run, args=(run_and_store_result(),))
    t.start()
    t.join()
    return result

do_things a continuación, puede tener este aspecto:

async def do_things():
    sync_stuff()
    run_async(async_func())
    more_sync_stuff()

Será exigible tanto de código síncronas y asíncronas, pero el costo será que:

  • se creará un nuevo evento de bucle de cada uno y cada una. (Aunque se puede almacenar en caché el bucle de eventos y nunca salir de él).
  • cuando se llama desde el código asíncrono, bloqueará bucle de eventos de la persona que llama, rompiendo así con eficacia su uso asyncio, aunque la mayor parte de tiempo se gasta realmente en el interior de su propio código asíncrono.

Supongo que te gusta

Origin http://10.200.1.11:23101/article/api/json?id=377311&siteId=1
Recomendado
Clasificación