在Python中执行定期操作

我正在使用Windows。 我想每10秒执行一个函数foo()

我该怎么做呢?

foo()的末尾,创build一个Timer ,它在10秒后调用foo()本身。
因为, Timer创build一个新thread来调用foo()
你可以做其他的东西而不被封锁。

 import time, threading def foo(): print(time.ctime()) threading.Timer(10, foo).start() foo() #output: #Thu Dec 22 14:46:08 2011 #Thu Dec 22 14:46:18 2011 #Thu Dec 22 14:46:28 2011 #Thu Dec 22 14:46:38 2011 

简单地睡10秒或使用threading.Timer(10,foo)将导致开始时间漂移。 (你可能不关心这个问题,或者根据你的具体情况,它可能是问题的一个重要来源。)可能有两个原因 – 你的线程的唤醒时间不准确或者你的函数的执行时间不准确。

你可以在这篇文章的最后看到一些结果,但是首先是一个如何解决这个问题的例子。 你需要跟踪你的函数下一次被调用的时间,而不是实际被调用的时间和帐户的差异。

这是一个稍微漂移的版本:

 import datetime, threading def foo(): print datetime.datetime.now() threading.Timer(1, foo).start() foo() 

它的输出如下所示:

 2013-08-12 13:05:36.483580 2013-08-12 13:05:37.484931 2013-08-12 13:05:38.485505 2013-08-12 13:05:39.486945 2013-08-12 13:05:40.488386 2013-08-12 13:05:41.489819 2013-08-12 13:05:42.491202 2013-08-12 13:05:43.492486 2013-08-12 13:05:44.493865 2013-08-12 13:05:45.494987 2013-08-12 13:05:46.496479 2013-08-12 13:05:47.497824 2013-08-12 13:05:48.499286 2013-08-12 13:05:49.500232 

你可以看到,亚秒数不断增加,因此,开始时间是“漂移”。

这是正确解释漂移的代码:

 import datetime, threading, time next_call = time.time() def foo(): global next_call print datetime.datetime.now() next_call = next_call+1 threading.Timer( next_call - time.time(), foo ).start() foo() 

它的输出如下所示:

 2013-08-12 13:21:45.292565 2013-08-12 13:21:47.293000 2013-08-12 13:21:48.293939 2013-08-12 13:21:49.293327 2013-08-12 13:21:50.293883 2013-08-12 13:21:51.293070 2013-08-12 13:21:52.293393 

在这里你可以看到亚秒级不再有任何增加。

如果您的事件发生得非常频繁,您可能需要在单个线程中运行计时器,而不是为每个事件启动一个新的线程。 虽然考虑漂移,这将看起来像:

 import datetime, threading, time def foo(): next_call = time.time() while True: print datetime.datetime.now() next_call = next_call+1; time.sleep(next_call - time.time()) timerThread = threading.Thread(target=foo) timerThread.start() 

但是,您的应用程序不会正常退出,您将需要杀死计时器线程。 如果你想在你的应用程序完成时正常退出,而不需要手动杀死线程,你应该使用

 timerThread = threading.Thread(target=foo) timerThread.daemon = True timerThread.start() 

感到惊讶的是没有find一个解决scheme使用发电机进行计时。 我只是为了自己的目的而devise的。

这个解决scheme:单线程,每个周期没有对象实例化,使用发生器的时间,稳定的时间精度的time模块(不像我从栈交换尝试的几个解决scheme)。

注意:对于Python 2.x,用g.next()replacenext(g) g.next()

 import time def do_every(period,f,*args): def g_tick(): t = time.time() count = 0 while True: count += 1 yield max(t + count*period - time.time(),0) g = g_tick() while True: time.sleep(next(g)) f(*args) def hello(s): print('hello {} ({:.4f})'.format(s,time.time())) time.sleep(.3) do_every(1,hello,'foo') 

结果在,例如:

 hello foo (1421705487.5811) hello foo (1421705488.5811) hello foo (1421705489.5809) hello foo (1421705490.5830) hello foo (1421705491.5803) hello foo (1421705492.5808) hello foo (1421705493.5811) hello foo (1421705494.5811) hello foo (1421705495.5810) hello foo (1421705496.5811) hello foo (1421705497.5810) hello foo (1421705498.5810) hello foo (1421705499.5809) hello foo (1421705500.5811) hello foo (1421705501.5811) hello foo (1421705502.5811) hello foo (1421705503.5810) 

请注意,这个例子包含了一个cpu的模拟,每个周期做了0.3秒。 如果你每次把它改成随机都没有关系。 在被调用的函数比指定的时间长的情况下, yield线中的最大值用于保护来自负数的sleep 。 在这种情况下,它会立即执行,弥补下一次执行时间的损失。

也许调度模块将满足您的需求。

或者,考虑使用一个Timer对象 。

下面是使用Thread类的一个很好的实现: http : //g-off.net/software/a-python-repeatable-threadingtimer-class

下面的代码更加快速和肮脏:

 from threading import Timer from time import sleep def hello(): print "hello, world" t = Timer(3,hello) t.start() t = Timer(3, hello) t.start() # after 3 seconds, "hello, world" will be printed # timer will wake up ever 3 seconds, while we do something else while True: print "do something else" sleep(10) 

这是一个简单的单线程睡眠版本,它会漂移,但会在检测到漂移时尝试自动更正。

注意:只有满足以下3个合理的假设才能使用:

  1. 时间周期远远大于正在执行的函数的执行时间
  2. 正在执行的function在每个呼叫上花费大致相同的时间量
  3. 通话之间的漂移量不到一秒钟

 from datetime import timedelta from datetime import datetime def exec_every_n_seconds(n,f): first_called=datetime.now() f() num_calls=1 drift=timedelta() time_period=timedelta(seconds=n) while 1: time.sleep(n-drift.microseconds/1000000.0) current_time = datetime.now() f() num_calls += 1 difference = current_time - first_called drift = difference - time_period* num_calls print "drift=",drift 

这将在foo()每次调用之间插入一个10秒钟的hibernate时间,这大概是您在快速完成呼叫时所要求的。

 import time while True: foo() time.sleep(10) 

编辑:当您的foo()在后台线程中被调用时做其他事情

 import time import sys import threading def foo(): sys.stdout.write('({}) foo\n'.format(time.ctime())) def foo_target(): while True: foo() time.sleep(10) t = threading.Thread(target=foo_target) t.daemon = True t.start() raw_input('do other things...') 

你可以在不同的线程中执行你的任务。 threading.Timer会让你在一段时间之后执行给定的callback,例如,如果你想执行你的任务,只要callback返回True (这实际上是glib.timeout_add提供的,但你可能没有它安装在Windows中)或直到您取消它,您可以使用此代码:

 import logging, threading, functools import time logging.basicConfig(level=logging.NOTSET, format='%(threadName)s %(message)s') class PeriodicTimer(object): def __init__(self, interval, callback): self.interval = interval @functools.wraps(callback) def wrapper(*args, **kwargs): result = callback(*args, **kwargs) if result: self.thread = threading.Timer(self.interval, self.callback) self.thread.start() self.callback = wrapper def start(self): self.thread = threading.Timer(self.interval, self.callback) self.thread.start() def cancel(self): self.thread.cancel() def foo(): logging.info('Doing some work...') return True timer = PeriodicTimer(1, foo) timer.start() for i in range(2): time.sleep(2) logging.info('Doing some other work...') timer.cancel() 

示例输出:

 Thread-1 Doing some work... Thread-2 Doing some work... MainThread Doing some other work... Thread-3 Doing some work... Thread-4 Doing some work... MainThread Doing some other work... 

注意:callback不是每间隔执行一次。 Interval是线程在上次完成callback和下一次callback之间等待的时间。

如果你打算每10秒在一个python脚本中运行foo(),你可以在这些行上做一些事情。

 import time def foo(): print "Howdy" while True: foo() time.sleep(10)