如何持续等待多个并发任务(如多连接套接字)中的任意一个完成?
多套接字持续监控并响应的正确实现方式
要持续监控多个套接字、响应任意一个的可读事件,最优雅且正确的方式是利用asyncio的协程调度特性,避免手动取消待处理任务的低效做法。以下是两种推荐方案:
方案一:为每个套接字创建独立的持续读取协程
这是最简洁的实现方式,每个套接字单独维护自己的读取循环,事件循环会自动调度所有协程,无需手动管理任务列表:
async def read_and_process(sock): while True: data = await sock.read() if not data: # 套接字连接关闭,退出循环 break # 处理读取到的数据 handle_received_data(data) async def main(): # 假设已初始化好socket1、socket2等套接字对象 await asyncio.gather( read_and_process(socket1), read_and_process(socket2) )
每个read_and_process协程会持续等待对应套接字的数据,读取完成后立即处理,随后自动回到等待状态。事件循环会在任意套接字有数据时唤醒对应的协程,完全无需手动干预任务的重新加入。
方案二:动态维护任务集合(适用于需动态增减套接字的场景)
如果需要随时添加或移除监控的套接字,可以维护一个活跃任务集合,处理完成单个套接字的读取后,重新提交该套接字的读取任务,同时保留其他待处理任务(绝对不要取消pending任务):
async def main(): # 初始化套接字与任务的映射,方便后续关联任务和对应套接字 sockets = [socket1, socket2] task_to_sock = {} tasks = set() # 初始化为每个套接字创建读取任务 for sock in sockets: task = asyncio.create_task(sock.read()) task_to_sock[task] = sock tasks.add(task) while tasks: done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) for task in done: sock = task_to_sock.pop(task) try: data = task.result() if data: # 处理数据 handle_received_data(data) # 重新提交该套接字的读取任务,继续监控 new_task = asyncio.create_task(sock.read()) task_to_sock[new_task] = sock pending.add(new_task) else: # 连接关闭,不再监控该套接字 print(f"套接字{sock}已关闭") except Exception as e: # 处理读取异常 print(f"读取套接字{sock}出错: {str(e)}") # 更新任务集合为包含新任务的pending集合 tasks = pending
为什么你的临时方案有问题
你之前每次循环取消pending任务的做法是错误的:这些pending任务正处于等待其他套接字数据的状态,取消后会导致这些套接字的监控中断,必须重新创建任务才能继续监控,不仅浪费资源,还可能在取消与重建的间隙错过数据,同时会触发不必要的CancelledError异常处理。
内容的提问来源于stack exchange,提问作者JanLikar
相关产品推荐
相关产品推荐

