如何在特定的时间执行程序

4

我有一个需要在一定时间间隔内执行的程序。比如说,我可能希望它每五分钟执行一次。我有几个协调器与多个终端节点设备进行通信。下面的代码位于协调器上。如果间隔设置为5,则需要在例如9:05、9:10、9:15、9:20、9:25等时刻运行并记录信息。到目前为止,我的代码如下:

if __name__ == '__main__':
    while True:
        try:
            r = json.read(rqst_command())
            interval = r.get('intvl')
            collect_time = r.get('c_time')
            command = r.get('cmd')
            send_stats(cmd_nodes(command, collect_time))
            time.sleep(interval)
        except Exception, e:
            print e
            print "**Top Level Exception"
            pass

问题在于,如果我将间隔设置为5分钟,它并不会精确地每5分钟记录一次。执行时间似乎会慢慢增加这个时间。例如,上面的代码可能记录为9:05:09, 9:10:19, 9:15:29, 9:20:41, 9:25:50。程序运行所需的时间取决于节点通信的速度。
有人有什么想法吗?我该如何更改代码,以便程序能够精确地每5分钟执行一次?
编辑/更新
我想我已经找到了解决我的问题的方法。我获取当前的datetime,然后检查它是否在5分钟标记上。如果是,则记录datetime并将其发送到send_stats函数。这样datetime将始终是我想要的。如果不是在5分钟标记上,则sleep一段时间,然后再次检查。我已经基本完成了代码。但是,当我运行程序时,我收到以下错误:'builtin_function_or_method' object has no attribute 'year'
我做错了什么?
下面是我的新代码:
import os
import json
import datetime
from datetime import datetime
import urllib2
from urllib import urlencode
from socket import *
import time
import zigbee
import select

if __name__ == '__main__':
        while True:
            try:
                r = json.read(rqst_command())
                interval = r.get('intvl')
                collect_time = r.get('c_time')
                command = r.get('cmd')

                tempTest = True

                while tempTest == True:
                    start_time = datetime.now
                    compare_time = datetime(start_time.year, start_time.month, start_time.day, start_time.hour, 0, 0)
                    difff = int((start_time - compare_time).total_seconds() / 60)

                    if((difff % interval) == 0):
                        c_t = datetime(start_time.year, start_time.month, start_time.day, start_time.hour, start_time.minute, 0)
                        send_stats(cmd_nodes(command, collect_time), c_t)
                        tempTest = False
                    else:
                        time.sleep(30)              
            except Exception, e:
                print e
                print "**Top Level Exception"
                pass

5
你考虑过使用类似于cron的任务调度器吗? - SirDarius
会不会更容易在操作系统层面上处理,比如设置一个cron job?编辑:SirDarius比我先回答了! - David B
运行该程序的协调器实际上是一台Digi ConnectPort X4。我认为cron不是一个选项。 - Linger
请向我们展示完整的回溯信息。 - Dr. Jan-Philip Gehrcke
我该如何获取完整的回溯信息?我正在使用Notepad++编辑Python代码,并在嵌入了Python的硬件上运行它。 - Linger
start_time = datetime.now是错误的。now是方法的名称,你必须调用它:start_time = datetime.now()。顺便说一句,在将代码放到特殊设备上之前,也许你应该在一个易于调试的平台上运行它。 - Dr. Jan-Philip Gehrcke
6个回答

3
以下代码是解决我的问题的方法。现在无论间隔是多少,它都将始终使用每小时开始的datetime,并按照间隔增加。因此,如果间隔为5,则datetime显示为:例如:9:00:00、9:05:00、9:10:00、9:15:00、9:20:00、9:25:00等。如果间隔为3,则datetime显示为:例如:5:00:00、5:03:00、5:06:00、5:09:00、5:12:00、5:15:00等。协调器从端节点获取数据,然后将数据与datetime一起发送到远程服务器。
if __name__ == '__main__':
    last_minute = -1

    while True:
        try:
            r = json.read(rqst_command())
            print r
            command = r.get('cmd')
            interval = r.get('intvl')
            collect_time = r.get('c_time')

            tempTest = True

            while tempTest == True:
                start_time = datetime.now()
                s_minute = start_time.minute

                if(((s_minute % interval) == 0) and (s_minute != last_minute)):
                    tempTest = False
                    c_t = datetime(start_time.year, start_time.month, start_time.day, start_time.hour, start_time.minute, 0)
                    last_minute = c_t.minute

                    send_stats(cmd_nodes(command, collect_time), c_t)
                    time.sleep(1)
                else:
                    time.sleep(1)
        except Exception, e:
            print e
            print "**Top Level Exception"
            pass

2

怎么样:

while true:
    try:

        start=time.time() # save the beginning time before execution starts

        r = json.read(rqst_command())
        interval = r.get('intvl')
        collect_time = r.get('c_time')
        command = r.get('cmd')
        start_time = datetime.now()
        send_stats(cmd_nodes(command, collect_time))
        end_time = datetime.now()
        sleepy_time = interval - (end_time - start_time)

        while time.time() <= start + sleepy_time*60: #To wait until interval has ended note: I'm assuming sleepy_time is in minutes.
            pass

    except Exception, e:
    print e
    print "**Top Level Exception"
    pass

2
如其他人所指出的,已经有像cron这样的现成的工作/任务调度器可供使用。您可以直接使用它们。但是您也可以在Python中实现自己的简单解决方案,这也是可以的。您只需要以正确的方式进行操作。您方法中的根本问题是,在操作之间休眠一定时间并且不定期检查系统时间。在您的方法中,操作的持续时间是您的时间测量误差。而且这个误差会随着每个操作而累积。您需要一个没有这种误差的时间参考,即系统时间。
实现示例: 例如,假设秒级精度对您来说足够好。然后在循环内每秒检查一次系统时间。您可以通过time.sleep(1)安全地实现这一点。如果系统时间比last_action_execution_time(您显然已经在某个地方存储了它)晚5分钟,则将当前时间存储为last_action_execution_time并执行操作。只要此操作肯定持续时间少于5分钟,下一次执行就会发生在last_action_execution_time + 5 min,仅具有非常小的误差。最重要的是,这个误差在运行时不会随着执行次数增加而增长。
对于一个非常可靠的基于Python的解决方案,你还应该看一下http://docs.python.org/library/sched.html

1

我假设您想在Python中完成此操作,而不依赖于任何其他系统。

您只需要考虑进程开始和结束的时间,并相应地设置时间间隔。代码将类似于以下内容。

if __name__ == '__main__':
while True:
    try:
        r = json.read(rqst_command())
        interval = r.get('intvl')
        collect_time = r.get('c_time')
        command = r.get('cmd')
        start_time = datetime.now()
        send_stats(cmd_nodes(command, collect_time))
        end_time = datetime.now()
        sleepy_time = interval - (end_time - start_time)
        time.sleep(sleepy_time)
    except Exception, e:
        print e
        print "**Top Level Exception"
        pass

1

如果您使用Linux,您可能希望设置一个cronjob,以在特定的时间间隔内运行您的脚本。


cron 在我使用的设备上不可用。 - Linger

1

有两种方法可以做到这一点。

第一种,也是最好的方法是使用操作系统的任务计划程序(Windows中的任务计划程序,Linux中的cron)。这些工具的开发人员可能预料到了比你想象的更多的问题,并且编写了你不必自己编写的代码,这样可以节省时间和避免错误。

否则,你需要考虑脚本的执行时间。最简单的方法是,不要在间隔期间休眠(正如你看到的那样慢慢向前滑动),而是根据上次唤醒的时间计算下一次执行的时间,并在执行后仅休眠现在和那时之间的间隔时间。


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