【问题标题】:Asyncio keyboard input that can be canceled可以取消的异步键盘输入
【发布时间】:2019-10-21 20:12:20
【问题描述】:

我正在尝试使用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()

【问题讨论】:

  • 你必须避免 readline,因为它完全按照你说的做,即等待输入,并且你希望你的键盘输入不等待输入。你还试过什么。
  • @barny:选项不多。我试过input()msvcrt.getwch()。效果一样。

标签: python python-asyncio


【解决方案1】:

问题在于执行者坚持要确保所有正在运行的期货在程序终止时都已完成。但在这种情况下,您实际上想要一个“不干净”的终止,因为没有可移植的方式来取消正在进行的 read() 或异步访问 sys.stdin

取消未来没有任何效果,因为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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-20
    • 2011-04-20
    • 2013-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多