我正在尝试使用 asyncio
编写并发 Python 程序,该程序也接受键盘输入。当我尝试关闭我的程序时出现问题。由于键盘输入最终是通过 sys.stdin.readline
完成的,该函数仅在我按下 ENTER
后返回,无论我是否 stop()
事件循环或 cancel()
函数的 Future
。
有什么方法可以提供可以取消的asyncio
键盘输入吗?
这是我的 MWE。它将接受键盘输入 1 秒,然后 stop()
:
import asyncio
import sys
async def console_input_loop():
while True:
inp = await loop.run_in_executor(None, sys.stdin.readline)
print(f"[{inp.strip()}]")
async def sleeper():
await asyncio.sleep(1)
print("stop")
loop.stop()
loop = asyncio.get_event_loop()
loop.create_task(console_input_loop())
loop.create_task(sleeper())
loop.run_forever()
最佳答案
问题在于执行者坚持确保在程序终止时所有正在运行的 future 都已完成。但在这种情况下,您实际上想要一个“不干净”的终止,因为没有可移植的方法来取消正在进行的 read()
或异步访问 sys.stdin
。
取消 future 没有效果,因为 concurrent.futures.Future.cancel
一旦它的回调开始执行,它就是一个空操作。避免不必要的等待的最佳方法是首先避免 run_in_executor
并生成您自己的线程:
async def ainput():
loop = asyncio.get_event_loop()
fut = loop.create_future()
def _run():
line = sys.stdin.readline()
loop.call_soon_threadsafe(fut.set_result, line)
threading.Thread(target=_run, daemon=True).start()
return await fut
该线程是手动创建的并标记为“守护进程”,因此在程序关闭时没有人会等待它。因此,使用 ainput
而不是 run_in_executor(sys.stdin.readline)
的代码变体按预期终止:
async def console_input_loop():
while True:
inp = await ainput()
print(f"[{inp.strip()}]")
# rest of the program unchanged
关于python - 可取消的 Asyncio 键盘输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58493467/