asyncio.gather启动前两个任务,第三个UDP发送任务未执行
问题原因分析
你的第三个任务GcuUdpClient.send_packets()无法执行,核心原因是**PostServer.run_server()中的同步阻塞操作卡死了asyncio事件循环**:
HTTPServer.serve_forever()是同步阻塞方法,会持续占用当前线程,不会让出CPU给asyncio事件循环调度其他任务。你在它前面加的await完全无效,因为该方法并非可等待对象(协程/async函数),导致事件循环被彻底阻塞,GcuUdpClient.send_packets()根本没机会被调度执行。- 另外
GrpcServer.run()虽为async函数,但server.start()是非阻塞启动,函数执行后会立即返回,任务很快结束,但这不是当前主问题的诱因。
修复方案
1. 修复PostServer,避免阻塞事件循环
把同步的HTTPServer放到独立线程中运行,不占用asyncio事件循环:
修改http_server.py中的PostServer.run_server方法:
class PostServer: @staticmethod async def run_server(server_class=HTTPServer, handler_class=SimpleHTTPRequestHandler, addr=HTTP_SERVER_IP, port=HTTP_PORT_NUMBER): server_address = (addr, port) print('Http Post server : {0}:{1}'.format(addr, port)) httpd = server_class(server_address, handler_class) # 用守护线程运行同步的serve_forever,不阻塞事件循环 thread = threading.Thread(target=httpd.serve_forever, daemon=True) thread.start() # 让async函数保持运行状态 await asyncio.Event().wait()
2. 修复GrpcServer.run,让async函数持续运行
当前GrpcServer.run()在server.start()后直接返回,任务会立即结束,需让它持续运行至服务器终止:
修改grpc_server.py中的GrpcServer.run方法:
class GrpcServer: @staticmethod async def run(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) # 修正笔误:将OServices()改为OPSStationServices() guidance_grpc_service_definitions_pb2_grpc.add_OpRPCServicesServicer_to_server(OPSStationServices(), server) server.add_insecure_port('[::]:{0}'.format(GRPC_PORT_NUMBER)) print('Grpc Server : {0}:{1}'.format('localhost', GRPC_PORT_NUMBER)) server.start() # 等待服务器终止,保持async函数运行 await server.wait_for_termination()
3. 实现GcuUdpClient的循环发送逻辑
原始代码仅发送一次数据包,若要循环异步发送,需添加循环并使用asyncio原生UDP API:
修改gcu_udp_client.py中的send_packets方法:
class GcuUdpClient: @staticmethod async def send_packets(): data = get_data_packet() # 使用asyncio UDP客户端,避免同步操作阻塞事件循环 transport, _ = await asyncio.get_event_loop().create_datagram_endpoint( lambda: asyncio.DatagramProtocol(), remote_addr=server_address ) try: while True: transport.sendto(data) print('sending data packets') await asyncio.sleep(5) finally: transport.close()
4. 验证主函数
修复后的三个任务可通过asyncio.gather正常并行运行:
async def main(): await asyncio.gather( GrpcServer.run(), PostServer.run_server(), GcuUdpClient.send_packets() ) if __name__ == '__main__': asyncio.run(main())
额外注意点
- 原
grpc_server.py中add_OpRPCServicesServicer_to_server传入的OServices()是笔误,需修正为导入的OPSStationServices(),否则会触发未定义错误。 - 同步socket操作虽单次阻塞时间短,但严格遵循异步规范的话,建议使用asyncio提供的网络API,避免意外阻塞事件循环。
内容的提问来源于stack exchange,提问作者liv2hak
相关产品推荐
相关产品推荐

