如何让subprocess启动程序后返回控制权?非阻塞运行WebSocket服务器?
解决方案
你的问题核心是asyncio.get_event_loop().run_forever()会阻塞当前线程,导致后面的subprocess代码永远无法执行。下面提供两种可行的解决思路:
思路一:先启动子进程,再运行WebSocket服务器
把启动子进程的代码移到事件循环启动前,并且用subprocess.Popen()替代subprocess.call()——因为call()是阻塞式调用,会等待子进程结束才返回,而Popen()会直接启动子进程并后台运行,不阻塞当前脚本的执行。
修改后的代码:
#!python3 import os import subprocess import asyncio import websockets async def hello(websocket, path): name = await websocket.recv() print("< {}".format(name)) greeting = "Hello {}!".format(name) await websocket.send(greeting) print("> {}".format(greeting)) start_server = websockets.serve(hello, 'localhost', 8765) # 先启动后台子进程,不阻塞后续代码 subprocess.Popen(['foo.exe', 'd:\\data\\foo_data.txt']) # 启动WebSocket服务器并进入事件循环 asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()
思路二:用asyncio原生方式管理子进程(推荐)
把启动子进程的操作也放到asyncio的事件循环中,这样可以统一管理WebSocket服务器和子进程的生命周期,还能方便后续和子进程进行异步交互(比如读取输出、发送指令)。
修改后的代码:
#!python3 import asyncio import websockets async def run_subprocess(): # 异步启动子进程,不阻塞事件循环 process = await asyncio.create_subprocess_exec( 'foo.exe', 'd:\\data\\foo_data.txt', stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) # 如果不需要等待子进程结束,注释掉下面一行即可让子进程后台运行 # await process.wait() async def hello(websocket, path): name = await websocket.recv() print("< {}".format(name)) greeting = "Hello {}!".format(name) await websocket.send(greeting) print("> {}".format(greeting)) async def main(): # 启动WebSocket服务器 server = await websockets.serve(hello, 'localhost', 8765) # 启动子进程 await run_subprocess() # 保持事件循环持续运行(通过等待一个永远不会触发的事件) await asyncio.Event().wait() if __name__ == "__main__": asyncio.run(main())
关键说明
- 不要用
subprocess.call()启动需要长期运行的应用:它会阻塞当前进程,直到子进程退出,完全不符合你的需求。 - 事件循环的
run_forever()是阻塞调用:一旦执行,后面的代码都不会被执行,必须把需要提前做的操作放在它之前,或者把所有逻辑都整合到事件循环的异步任务中。
内容的提问来源于stack exchange,提问作者easythrees
相关产品推荐
相关产品推荐

