从Asyncio.Protocol.data_Received调用协程

2022-03-25 00:00:00 python python-asyncio

问题描述

这与Calling coroutines in asyncio.Protocol.data_received类似,但我认为需要提出新问题。

我有一个简单的服务器设置如下

loop.create_unix_server(lambda: protocol, path=serverSocket)

如果我这样做,它工作得很好

 def data_received(self, data):
    data = b'data reply'
    self.send(data)

我的客户端收到回复。但是我无法让它与任何类型的asyncio调用一起工作。我尝试了以下所有方法,但都不起作用。

@asyncio.coroutine
def go(self):
    yield from asyncio.sleep(1, result = b'data reply')

def data_received(self, data):
    print('Data Received', flush=True)

    task = asyncio.get_event_loop().create_task(self.go())
    data = yield from asyncio.wait_for(task,10)
    self.send(data)

如果我用@asyncio.coroutine装饰了data_received,则没有打印任何内容(如果我用@asyncio.coroutine装饰的话)好的,我知道data_received中使用Year是不正确的。

如果我尝试新的事件循环,如下所示,它将挂起run_until_complete

    loop = asyncio.new_event_loop()
    task = loop.create_task(self.go())
    loop.run_until_complete(task)
    data = task.result()
    self.send(data)

如果我使用Future,它也挂在run_until_complete

@asyncio.coroutine
def go(self, future):
    yield from asyncio.sleep(1)
    future.set_result(b'data reply')

def data_received(self, data):
    print('Data Received', flush=True)

    loop = asyncio.new_event_loop()
    future = asyncio.Future(loop=loop)
    asyncio.async(self.go(future))
    loop.run_until_complete(future)
    data = future.result()
    self.send(data)

以下代码接近,但它立即返回,并且结果类型为asyncio.coroutines.CoroWrapper,表示wait_for行立即返回未完成的任务?

@asyncio.coroutine
def go(self):
    return(yield from asyncio.sleep(3, result = b'data reply'))

@asyncio.coroutine
def go2(self):
    task = asyncio.get_event_loop().create_task(self.go())
    res = yield from asyncio.wait_for(task, 10)
    return result

def data_received(self, data):
    print('Data Received', flush=True)

    data = self.go2()
    self.send(data)

我真的有点手足无措,希望能给我一些建议,告诉我应该看些什么。


解决方案

您需要将协程添加到事件循环中,然后使用Future.add_done_callback在协程完成时处理结果:

@asyncio.coroutine
def go(self):
    return(yield from asyncio.sleep(3, result = b'data reply'))

def data_received(self, data):
    print('Data Received', flush=True)

    task = asyncio.async(self.go()) # or asyncio.get_event_loop().create_task()
    task.add_done_callback(self.handle_go_result)

def handle_go_result(self, task):
    data = task.result()
    self.send(data)

直接在data_received中调用协程是不允许的,因为调用方不会尝试yield from,并且在data_received内创建/运行新的事件循环将始终阻塞主事件循环,直到内部事件循环完成其工作。

您只想安排一些主事件循环的工作(asyncio.async/loop.create_task()),并安排一个回调在工作完成时运行(add_done_callback)。

相关文章