es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Python3.6 cocotb coroutine: llamar a una función asincrónica que contiene “yield” en una función sincrónica.

Me encontré con algunos problemas al intentar hacer una función síncrona que llama a funciones asíncronas. (Python 3.6.9, cocotb 1.4.0)

Como se muestra en el siguiente código de ejemplo, la función “read_cb” llamará a la función “read” (en la clase “FakeDriver”).

Después de ejecutarlo, obtengo el error:

yield self._fake_lock()
RuntimeError: Task got bad yield: <cocotb.decorators.runningcoroutine object="" at="" 0x7f7fecdbfe10="">

Lo que quiero es:

init FakerDriver
bloqueando...
bloqueo completado
leer...
dirección: 0x01
desbloqueando...
desbloqueo completado
lectura completada
import cocotb
import asyncio
from cocotb.decorators import coroutine
from cocotb.triggers import Event

class FakeDriver():
    def __init__(self):
        print("init FakeDriver")
        self.busy_event = Event("driver_busy")
        self.busy = False

@coroutine
def read(self, addr):
    print("leer...")

    yield self._fake_lock()
    print("dirección:", addr)
    self._fake_unlock()

    print("lectura completada")

@coroutine
def _fake_lock(self):
    print("bloqueando...")
    if self.busy:
        yield self.busy_event.wait()

    self.busy_event.clear()
    self.busy = True
    print("bloqueo completado")

def _fake_unlock(self):
    print("desbloqueando...")
    self.busy = False
    self.busy_event.set()
    print("desbloqueo completado")

def read_cb():
    dri = FakeDriver()
    loop = asyncio.get_event_loop()
    task = loop.create_task(dri.read("0x01"))
    ret = loop.run_until_complete(task)
    loop.close()

if __name__ == "__main__":
    read_cb()

</cocotb.decorators.runningcoroutine>

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    El problema con el código proporcionado es que read_cb es una función síncrona que llama a una función asíncrona _fake_lock que utiliza la palabra clave yield para suspender la ejecución de la corutina hasta que se resuelva un futuro.

    Para llamar a una función asíncrona desde una función síncrona, necesitamos utilizar la función asyncio.run(), que es la encargada de ejecutar un bucle de eventos.

    Entonces, la función read_cb necesita ser modificada de la siguiente manera:

    import asyncio
    
    def read_cb():
        dri = FakeDriver()
        asyncio.run(dri.read("0x01"))
    
    if __name__ == "__main__":
        read_cb()
    

    Observa que ya no es necesario crear y cerrar el bucle de eventos, ya que asyncio.run() se encarga de eso.

    Esta modificación debería permitir que se imprima la salida esperada:

    init FakeDriver
    bloqueando...
    bloqueo terminado
    leyendo...
    dir:  0x01
    desbloqueando...
    desbloqueo terminado
    lectura terminada
    

Comments are closed.