python - 如何制作可以由后台线程终止的 Python CLI input() 循环?
问题描述
编辑:澄清这适用于 Python 3。SO 上的其他一些帖子适用于 Python 2,不适用于 Python 3。
我正在用 Python 编写一个机器人。该机器人将启动一些后台线程来执行诸如轮询服务、执行 API 调用等操作。该机器人的主线程是一个 CLI,它实现了一种“REPL”循环(输入命令和参数,会发生一些事情,等待另一个命令,冲洗并重复。)
机器人需要终止的情况有两种:
- 如果用户键入退出命令。在这种情况下,主循环将设置标志告诉处理线程终止并加入这些线程;一旦所有线程终止,应用程序就会正确退出。
- 如果其中一个处理线程决定机器人需要终止。例如,如果管理员通过外部界面而不是通过 CLI 请求机器人断开连接。
这是一些非常粗略的伪代码:
import threading
class Bot:
KeepRunning = True
def api_thread(self):
# we can assume this loop will execute at least once per second or so
while self.KeepRunning:
shouldQuit = pollTheApiAndDoThings()
if shouldQuit:
self.KeepRunning = False
return
def service_thread(self):
# we can assume this loop will execute at least once per second or so
while self.KeepRunning:
shouldQuit = connectToAServiceAndDoThings()
if shouldQuit:
self.KeepRunning = False
return
def stop(self):
self.KeepRunning = False
self._api_thread.join()
self._service_thread.join()
def run(self):
self._api_thread = threading.Thread(target=self.api_thread)
self._service_thread = threading.Thread(target=self.service_thread)
self._api_thread.start()
self._service_thread.start()
if __name__ == "__main__":
b = Bot()
b.run()
while b.KeepRunning:
cmd = input("> ")
if cmd.lower().rstrip()=="quit":
b.stop() # wait for bot to stop
exit(0)
elif ... # process all other user commands
# if keepRunning got set to false while we were in the input loop, don't repeat the loop.
# however, we want some way to end the input() call immediately if keepRunning gets set false.
print("Remote shutdown requested. Exiting now.")
exit(0)
如果用户键入quit
. 每个线程将在其下一次迭代中退出,机器人将退出。
但是,如果机器人线程之一需要告诉机器人关闭,程序将保持运行,直到用户至少按一次 Enter。此外,由于机器人已经基本终止,任何命令都需要被忽略。
如果没有 CLI,那么此应用程序将正常工作(例如,仅用 time.sleep(1) 替换输入循环,以便主线程保持活动状态,直到机器人线程请求退出)。所以,从逻辑上讲,我需要的是一种让input()
函数类似于循环运行的方式,该循环具有睡眠然后检查标志的想法。
我看过其他关于在 上执行超时的帖子input()
,但这里的不同之处在于没有特定的超时。只要机器人正在运行,我们就可以input()
根据需要阻止。但是一旦其中一个线程发出退出信号,就input()
需要取消调用。即使用户已经输入了一些内容,也可以忽略它,因为机器人已经关闭了。
这需要能够在具有相同代码的 Windows 和基于 Unix 的操作系统上工作。platform
用或类似的方式检查操作系统当然是可以接受的。
我考虑过使用像这样的方法并编写我自己的版本,input()
但这似乎非常麻烦且容易出错,尤其是跨平台。它还需要包含一个读取超时,这样它就不会变得与 相同input()
,而且我还需要手动编写诸如 Ctrl+C 处理之类的代码——不理想。而且它还会阻止通过标准输入传递字符串,例如使用子进程 - 因为据我所知,如果你正在进行原始终端交互(?)
解决方案
推荐阅读
- excel - 当文件名不同时,将数据从一个工作簿复制到另一个工作簿
- oracle - 动态更新引用其他表的字段?
- electron - 电子应用Mac应用商店图标问题->“缺少必需的图标”
- javascript - 单击时更改 Bootstrap 的折叠按钮的值
- node.js - 我有一个查询要从我的数据库中获取 2 件事,但它得到了一切。有人知道这里有什么问题吗?
- android - 添加对外部库的 androidx 支持
- c# - 当服务返回数据并更新模型对象时使用值更新按钮文本
- jenkins - 从内联函数加载声明性管道
- javascript - 如何更改第二个下拉列表的值
- regex - Laravel 正则表达式验证 - 找不到结束分隔符“/”