asyncio.Queue同步疑问:为何需await才能实现协程并发?
问题分析与解决
核心原因:协程的协作式调度机制
asyncio的协程是单线程协作式调度,事件循环只能在协程主动让出控制权时,才会切换到其他协程执行。
你的read协程里,while True循环大部分时间在空转检查port.in_waiting——这个操作是同步的,全程没有触发任何await语句。这就导致该协程一直霸占着事件循环,完全不给save_data协程运行的机会。
而当你加入await queue.join()或者await asyncio.sleep(0.001)时,await会让当前协程暂停执行,把控制权交还给事件循环。事件循环这时候才能调度save_data协程启动,两个协程也就可以并发运行了。
问题根源与优化方案
你的代码里还藏着一个隐患:使用了同步的serial库。虽然你先检查port.in_waiting再调用read_until避免了阻塞,但同步IO操作本身并不适配asyncio的调度模型。更合理的优化方案有两种:
方案1:使用异步串口库(推荐)
换成异步串口库比如aioserial,它的读取操作是异步的,天然支持await,能自动让出控制权:
# 安装:pip install aioserial import aioserial async def read(port: aioserial.AioSerial, queue: asyncio.Queue): print('Reading') while True: data = await port.read_until_async(EOL) id, code, *load, end_b1, end_b2 = data # 后续处理逻辑不变...
方案2:给同步操作加异步让步
如果必须使用同步serial库,在read协程的循环里添加一个最小的sleep,强制让出控制权:
async def read(port: serial.Serial, queue: asyncio.Queue): print('Reading') while True: if port.in_waiting > 0: # 原有的数据读取与处理逻辑... # 每次循环都让出控制权,给其他协程运行机会 await asyncio.sleep(0)
await asyncio.sleep(0)是一个轻量级的让步操作,不会引入实际延迟,只是告诉事件循环可以切换协程了。
额外优化:同步IO异步化
代码中的port.write也是同步操作,同样可能阻塞事件循环,可以用loop.run_in_executor包装成异步:
async def main(): # ...其他代码 loop = asyncio.get_running_loop() await loop.run_in_executor(None, port.write, bytearray([0x01])) # ...其他代码
内容的提问来源于stack exchange,提问作者Mariano Fernández
相关产品推荐
相关产品推荐

