与长时间运行的Python进程交互

3

我有一个长时间运行的python进程在树莓派上无头模式下运行(控制花园),如下所示:

from time import sleep

def run_garden():
    while 1:
        /* do work */
        sleep(60)

if __name__ == "__main__":
    run_garden()

60秒的睡眠时间足以处理我的花园中发生的所有更改(湿度、空气温度、泵开启、风扇关闭等),但是如果我想手动覆盖这些更改怎么办?
目前,在我的“/* do work */”循环中,我首先调用另一个服务器,该服务器保存配置变量,并且可以通过Web控制台更新这些配置变量,但它缺乏任何实时感觉,因为它依赖于60秒循环(例如,您可能会更新Web控制台,然后等待45秒以使期望效果生效)。
运行run_garden()的Raspberry Pi专用于花园,基本上是唯一占用资源的东西。所以我知道我有做某事的余地,但我不知道该做什么。
一旦循环发现已更新配置变量,循环就可以进行指数回退来继续检查交互,而不是等待60秒,但这似乎并没有太大改进。
是否有更好的方法来跳转到这个长时间运行的进程中?
3个回答

1
为什么不使用基于事件的循环,而是睡眠一定时间呢?
这样,只有在检测到更改时,您的循环才会运行,并且当检测到更改时它将始终运行(这就是您提出问题的原因?)。
您可以通过使用以下方式来实现:
python事件对象 只需等待触发一个或所有事件对象并运行循环。您还可以等待完成X个事件,等等,具体取决于您是否希望更新一个变量很多次。
甚至可以使用类似于以下系统: 广播事件

这个本来行得通,但最后我使用了一个简单的Web服务器,并设置了超时来一箭双雕。 - MattoTodd

1
在你的主循环中监听套接字。在套接字读取调用上使用超时(例如60秒,应执行下一个花园更新的时间),因此当没有命令进入时,至少每分钟返回到正常功能。
如果您需要花园管理更新不要比每分钟更快,您需要检查自上次更新以来的时间,因为当有命令进入时,读取调用将完成得更快。

1

Python的 select模块听起来可能会很有帮助。

如果你曾经使用过类Unix系统中的模块(例如在套接字编程中),那么这将会很熟悉。

如果没有, 这里是一个我经常推荐的C套接字参考文献中的 select 部分。而 这里则是看起来不错的模块说明

警告:第一个参考文献具体涉及C,而非Python,但select系统调用的概念是相同的,因此讨论可能会有所帮助。

基本上,它允许您告诉它您感兴趣的事件(例如,套接字数据到达、键盘事件等),并且它将阻塞,直到永久地或者直到您指定的超时时间到期为止。

如果您正在使用套接字,那么将套接字和标准输入添加到您感兴趣的事件列表中很容易。如果您只是想要一种“有条件地休眠”60秒的方法,除非/直到检测到按键,这也同样有效。
编辑:
另一种解决方法是让您的树莓派与运行Web控制台的服务器“注册”。这可能需要一些额外的工作,但它将为您提供所需的实时效果。
基本上,树莓派通过向服务器发出警报来“注册”自己,并且服务器存储设备的地址。如果使用TCP,您可以保持连接打开(如果您需要处理防火墙则可能很重要)。如果使用UDP,则可以在注册之前绑定设备上的端口,使服务器能够响应“公告”的源地址。
一旦公布,在服务器上更改配置选项时,通常会发生以下两件事之一:
A)向设备发送微小的“ping”(在一般意义上,而不是ICMP主机检测协议),通知其配置选项已更改。此时,主机将立即请求完整的配置设置,并获取更新。
你将更新的配置选项(或整个配置集)发送回设备。这会减少设备和服务器之间的消息数量,但这可能需要更多的工作,因为它似乎更偏离了你当前的设置。

如果在您的编辑中使用选项A,那么ping会命中第二个正在监听的进程,还是原始的garden进程(现在正在监听)? - MattoTodd
你可以用两种方式来实现,但我会选择后者。重构原始流程,首先进行注册,然后等待 ping(可能通过阻塞套接字调用)。一旦收到 ping,脚本就在那个点继续执行并获取配置集。 - jedwards
如果我因等待ping而被阻塞,那么我的程序如何每60秒继续运行循环呢?我有什么遗漏吗? - MattoTodd
不,我是。在这种情况下,我要么会采用多线程,要么简单地设置一个60秒的超时时间。如果您选择使用超时方案,那么唯一额外的延迟将会是当收到ping时正在进行主循环任务。在这种情况下,ping将在网络堆栈上缓存,并且在主循环任务完成后立即处理。 - jedwards

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接