Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/363.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 每x秒重复执行一个函数的最佳方式是什么?_Python_Timer - Fatal编程技术网

Python 每x秒重复执行一个函数的最佳方式是什么?

Python 每x秒重复执行一个函数的最佳方式是什么?,python,timer,Python,Timer,我希望永远每隔60秒在Python中重复执行一个函数(就像在Objective C中执行一个函数一样)。这段代码将作为守护进程运行,就像每分钟使用cron调用python脚本一样,但不需要用户进行设置 在中,解决方案似乎仅在x秒内有效。我不需要这样的高级功能,所以也许类似的东西可以工作 while True: # Code executed here time.sleep(60) 此代码是否存在任何可预见的问题?如果您的程序还没有事件循环,请使用实现通用事件调度器的模块 imp

我希望永远每隔60秒在Python中重复执行一个函数(就像在Objective C中执行一个函数一样)。这段代码将作为守护进程运行,就像每分钟使用cron调用python脚本一样,但不需要用户进行设置

在中,解决方案似乎仅在x秒内有效。我不需要这样的高级功能,所以也许类似的东西可以工作

while True:
    # Code executed here
    time.sleep(60)

此代码是否存在任何可预见的问题?

如果您的程序还没有事件循环,请使用实现通用事件调度器的模块

import sched, time
s = sched.scheduler(time.time, time.sleep)
def do_something(sc): 
    print("Doing stuff...")
    # do your stuff
    s.enter(60, 1, do_something, (sc,))

s.enter(60, 1, do_something, (s,))
s.run()

如果您已经在使用事件循环库,如
asyncio
trio
tkinter
PyQt5
gobject
kivy
,以及许多其他库,只需使用现有事件循环库的方法安排任务,相反。

它与cron之间的主要区别在于异常将永久杀死守护进程。你可能想用一个异常捕捉器和记录器来包装。

你可能想考虑哪一个是python网络库,它实现了.


虽然“While True:sleep(60)”可能会起作用,但Twisted可能已经实现了您最终需要的许多功能(后台监控、日志记录或异常处理,正如bobince所指出的),并且可能是一个更健壮的解决方案,我相信这是一种更简单的方法:

import time

def executeSomething():
    #code here
    time.sleep(60)

while True:
    executeSomething()
通过这种方式执行代码,然后等待60秒,然后再次执行,等待,执行,等等。。。
没有必要把事情复杂化:D

不久前我也遇到过类似的问题。可能有帮助吗

对于v0.2,以下代码段有效

import cronus.beat as beat

beat.set_rate(2) # 2 Hz
while beat.true():
    # do some time consuming work here
    beat.sleep() # total loop duration would be 0.5 sec

将时间循环锁定到系统时钟,如下所示:

import time
starttime = time.time()
while True:
    print "tick"
    time.sleep(60.0 - ((time.time() - starttime) % 60.0))

如果您希望使用非阻塞方式定期执行函数,而不是阻塞无限循环,我会使用线程计时器。这样,您的代码可以继续运行并执行其他任务,并且仍然每n秒调用一次您的函数。我经常使用这种技术来打印长时间、CPU/磁盘/网络密集型任务的进度信息

下面是我在类似问题中发布的代码,带有start()和stop()控件:

用法:

from time import sleep

def hello(name):
    print "Hello %s!" % name

print "starting..."
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
try:
    sleep(5) # your long-running job goes here...
finally:
    rt.stop() # better in a try/finally block to make sure the program ends!
特点:

  • 仅标准库,无外部依赖项
  • start()
    stop()
    可以安全地多次调用,即使计时器已经启动/停止
  • 要调用的函数可以具有位置参数和命名参数
  • 您可以随时更改
    间隔
    ,它将在下次运行后生效。对于
    args
    kwargs
    甚至
    函数也一样
    
以下是MestreLion的代码更新,可避免长时间滴水

这里的RepeatedTimer类按照OP的请求每隔“间隔”秒调用一次给定的函数;时间表并不取决于函数执行所需的时间。我喜欢这个解决方案,因为它没有外部库依赖关系;这只是纯python

import threading 
import time

class RepeatedTimer(object):
  def __init__(self, interval, function, *args, **kwargs):
    self._timer = None
    self.interval = interval
    self.function = function
    self.args = args
    self.kwargs = kwargs
    self.is_running = False
    self.next_call = time.time()
    self.start()

  def _run(self):
    self.is_running = False
    self.start()
    self.function(*self.args, **self.kwargs)

  def start(self):
    if not self.is_running:
      self.next_call += self.interval
      self._timer = threading.Timer(self.next_call - time.time(), self._run)
      self._timer.start()
      self.is_running = True

  def stop(self):
    self._timer.cancel()
    self.is_running = False
示例用法(摘自MestreLion的答案):


我使用它每小时引发60个事件,大多数事件在整分钟后以相同的秒数发生:

import math
import time
import random

TICK = 60 # one minute tick size
TICK_TIMING = 59 # execute on 59th second of the tick
TICK_MINIMUM = 30 # minimum catch up tick size when lagging

def set_timing():

    now = time.time()
    elapsed = now - info['begin']
    minutes = math.floor(elapsed/TICK)
    tick_elapsed = now - info['completion_time']
    if (info['tick']+1) > minutes:
        wait = max(0,(TICK_TIMING-(time.time() % TICK)))
        print ('standard wait: %.2f' % wait)
        time.sleep(wait)
    elif tick_elapsed < TICK_MINIMUM:
        wait = TICK_MINIMUM-tick_elapsed
        print ('minimum wait: %.2f' % wait)
        time.sleep(wait)
    else:
        print ('skip set_timing(); no wait')
    drift = ((time.time() - info['begin']) - info['tick']*TICK -
        TICK_TIMING + info['begin']%TICK)
    print ('drift: %.6f' % drift)

info['tick'] = 0
info['begin'] = time.time()
info['completion_time'] = info['begin'] - TICK

while 1:

    set_timing()

    print('hello world')

    #random real world event
    time.sleep(random.random()*TICK_MINIMUM)

    info['tick'] += 1
    info['completion_time'] = time.time()
但在60分钟结束时,你会有60个滴答声;其中大多数都会发生在您喜欢的时间的正确偏移处

在我的系统中,我得到了<1/20秒的典型漂移,直到需要修正为止

这种方法的优点是能够分辨时钟漂移;这可能会导致问题,如果你正在做的事情,如追加一个项目每勾,你希望每小时追加60个项目。不考虑漂移可能导致次要迹象,比如移动平均值,考虑过去太深的数据,导致错误的输出。 一个可能的答案:

import time
t=time.time()

while True:
    if time.time()-t>10:
        #run your task here
        t=time.time()

e、 显示当前本地时间

import datetime
import glib
import logger

def get_local_time():
    current_time = datetime.datetime.now().strftime("%H:%M")
    logger.info("get_local_time(): %s",current_time)
    return str(current_time)

def display_local_time():
    logger.info("Current time is: %s", get_local_time())
    return True

# call every minute
glib.timeout_add(60*1000, display_local_time)
我使用Tkinter after()方法,它不会“偷走游戏”(就像前面介绍的sched模块),也就是说,它允许其他东西并行运行:

import Tkinter

def do_something1():
  global n1
  n1 += 1
  if n1 == 6: # (Optional condition)
    print "* do_something1() is done *"; return
  # Do your stuff here
  # ...
  print "do_something1() "+str(n1)
  tk.after(1000, do_something1)

def do_something2(): 
  global n2
  n2 += 1
  if n2 == 6: # (Optional condition)
    print "* do_something2() is done *"; return
  # Do your stuff here
  # ...
  print "do_something2() "+str(n2)
  tk.after(500, do_something2)

tk = Tkinter.Tk(); 
n1 = 0; n2 = 0
do_something1()
do_something2()
tk.mainloop()
dosomething1()
dosomething2()
可以以任何间隔速度并行运行。这里,第二个将以两倍的速度执行。还要注意,我使用了一个简单的计数器作为终止任一函数的条件。您可以使用您喜欢的任何其他条件,或者如果您希望在程序终止之前运行函数(例如时钟),则可以不使用任何其他条件

如果要在不阻塞剩余代码的情况下执行此操作,可以使用此选项让其在自己的线程中运行:

import threading
threading.Thread(target=lambda: every(5, foo)).start()
此解决方案结合了其他解决方案中很少结合的几个功能:

  • 异常处理:在这个级别上,尽可能正确地处理异常,即。E在不中止程序的情况下,为调试目的登录
  • 无链接:您在许多答案中发现的常见链式实现(用于安排下一个事件)在以下方面是脆弱的,即如果调度机制(
    threading.Timer
    或其他什么)中出现任何错误,这将终止链。即使问题的原因已经确定,也不会发生进一步的处决。相比之下,使用简单的
    sleep()
    进行简单的循环和等待要健壮得多
  • 无漂移:我的解决方案精确跟踪它应该运行的时间。不存在依赖于执行时间的漂移(与许多其他解决方案一样)
  • 跳过:如果一次执行花费太多时间,我的解决方案将跳过任务(例如,每5秒执行一次X,但X花费6秒)。这是标准的cron行为(有很好的理由)。然后,许多其他解决方案只是连续几次执行该任务,而没有任何延迟。对于大多数情况(例如清理任务),这是不希望的。如果愿意,只需使用
    next\u time+=delay
    import time
    t=time.time()
    
    while True:
        if time.time()-t>10:
            #run your task here
            t=time.time()
    
    import datetime
    import glib
    import logger
    
    def get_local_time():
        current_time = datetime.datetime.now().strftime("%H:%M")
        logger.info("get_local_time(): %s",current_time)
        return str(current_time)
    
    def display_local_time():
        logger.info("Current time is: %s", get_local_time())
        return True
    
    # call every minute
    glib.timeout_add(60*1000, display_local_time)
    
    import Tkinter
    
    def do_something1():
      global n1
      n1 += 1
      if n1 == 6: # (Optional condition)
        print "* do_something1() is done *"; return
      # Do your stuff here
      # ...
      print "do_something1() "+str(n1)
      tk.after(1000, do_something1)
    
    def do_something2(): 
      global n2
      n2 += 1
      if n2 == 6: # (Optional condition)
        print "* do_something2() is done *"; return
      # Do your stuff here
      # ...
      print "do_something2() "+str(n2)
      tk.after(500, do_something2)
    
    tk = Tkinter.Tk(); 
    n1 = 0; n2 = 0
    do_something1()
    do_something2()
    tk.mainloop()
    
    import time, traceback
    
    def every(delay, task):
      next_time = time.time() + delay
      while True:
        time.sleep(max(0, next_time - time.time()))
        try:
          task()
        except Exception:
          traceback.print_exc()
          # in production code you might want to have this instead of course:
          # logger.exception("Problem while executing repetitive task.")
        # skip tasks if we are behind schedule:
        next_time += (time.time() - next_time) // delay * delay + delay
    
    def foo():
      print("foo", time.time())
    
    every(5, foo)
    
    import threading
    threading.Thread(target=lambda: every(5, foo)).start()
    
        ''' tracking number of times it prints'''
    import threading
    
    global timeInterval
    count=0
    def printit():
      threading.Timer(timeInterval, printit).start()
      print( "Hello, World!")
      global count
      count=count+1
      print(count)
    printit
    
    if __name__ == "__main__":
        timeInterval= int(input('Enter Time in Seconds:'))
        printit()
    
    class RepeatedTimer(object):
    def __init__(self, first_interval, interval, func, *args, **kwargs):
        self.timer      = None
        self.first_interval = first_interval
        self.interval   = interval
        self.func   = func
        self.args       = args
        self.kwargs     = kwargs
        self.running = False
        self.is_started = False
    
    def first_start(self):
        try:
            # no race-condition here because only control thread will call this method
            # if already started will not start again
            if not self.is_started:
                self.is_started = True
                self.timer = Timer(self.first_interval, self.run)
                self.running = True
                self.timer.start()
        except Exception as e:
            log_print(syslog.LOG_ERR, "timer first_start failed %s %s"%(e.message, traceback.format_exc()))
            raise
    
    def run(self):
        # if not stopped start again
        if self.running:
            self.timer = Timer(self.interval, self.run)
            self.timer.start()
        self.func(*self.args, **self.kwargs)
    
    def stop(self):
        # cancel current timer in case failed it's still OK
        # if already stopped doesn't matter to stop again
        if self.timer:
            self.timer.cancel()
        self.running = False
    
    import schedule
    import time
    
    def job():
        print("I'm working...")
    
    schedule.every(10).minutes.do(job)
    schedule.every().hour.do(job)
    schedule.every().day.at("10:30").do(job)
    schedule.every(5).to(10).minutes.do(job)
    schedule.every().monday.do(job)
    schedule.every().wednesday.at("13:15").do(job)
    schedule.every().minute.at(":17").do(job)
    
    while True:
        schedule.run_pending()
        time.sleep(1)
    
    import threading, time
    
    def print_every_n_seconds(n=2):
        while True:
            print(time.ctime())
            time.sleep(n)
        
    thread = threading.Thread(target=print_every_n_seconds, daemon=True)
    thread.start()
    
    
    #Tue Oct 16 17:29:40 2018
    #Tue Oct 16 17:29:42 2018
    #Tue Oct 16 17:29:44 2018
    
    pip install apscheduler
    
    from apscheduler.schedulers.background import BlockingScheduler
    def print_t():
      pass
    
    sched = BlockingScheduler()
    sched.add_job(print_t, 'interval', seconds =60) #will do the print_t work for every 60 seconds
    
    sched.start()
    
    import time
    
    while True:
        print("this will run after every 30 sec")
        #Your code here
        time.sleep(30)
    
    def print_every_5_seconds_have_passed_exit_eventually():
        """
        https://stackoverflow.com/questions/3393612/run-certain-code-every-n-seconds
        https://stackoverflow.com/questions/474528/what-is-the-best-way-to-repeatedly-execute-a-function-every-x-seconds
        :return:
        """
        opts = argparse.Namespace(start=time.time())
        next_time_to_print = 0
        while True:
            current_time_passed = time.time() - opts.start
            if current_time_passed >= next_time_to_print:
                next_time_to_print += 5
                print(f'worked and {current_time_passed=}')
                print(f'{current_time_passed % 5=}')
                print(f'{math.floor(current_time_passed % 5) == 0}')
    
    starting __main__ at __init__
    worked and current_time_passed=0.0001709461212158203
    current_time_passed % 5=0.0001709461212158203
    True
    worked and current_time_passed=5.0
    current_time_passed % 5=0.0
    True
    worked and current_time_passed=10.0
    current_time_passed % 5=0.0
    True
    worked and current_time_passed=15.0
    current_time_passed % 5=0.0
    True