快速精准的Python重复定时器
我需要快速而准确地从一个列表中发送重复的消息。这个列表需要每100毫秒发送一次消息,允许有±10毫秒的误差。我试过下面的代码,但问题是定时器等到100毫秒后才开始计算,这样就会导致定时器超出可接受的时间范围。
简单地减少等待时间是一种混乱且不可靠的解决办法。因为在消息循环中有一个锁,以防在循环过程中列表被编辑。
有没有什么办法可以让Python在大约100毫秒的时间内稳定地发送消息?谢谢
from threading import Timer
from threading import Lock
class RepeatingTimer(object):
def __init__(self,interval, function, *args, **kwargs):
super(RepeatingTimer, self).__init__()
self.args = args
self.kwargs = kwargs
self.function = function
self.interval = interval
self.start()
def start(self):
self.callback()
def stop(self):
self.interval = False
def callback(self):
if self.interval:
self.function(*self.args, **self.kwargs)
Timer(self.interval, self.callback, ).start()
def loop(messageList):
listLock.acquire()
for m in messageList:
writeFunction(m)
listLock.release()
MESSAGE_LIST = [] #Imagine this is populated with the messages
listLock = Lock()
rt = RepeatingTimer(0.1,loop,MESSAGE_LIST)
#Do other stuff after this
我明白writeFunction会造成一些延迟,但不会超过允许的10毫秒。我基本上需要每100毫秒调用一次这个函数来发送每条消息。消息列表很小,通常少于几个元素。
下一个挑战是让这个每10毫秒发送一次,误差在±1毫秒内 :P
3 个回答
试试这个:
#!/usr/bin/python
import time; # This is required to include time module.
from threading import Timer
def hello(start, interval, count):
ticks = time.time()
t = Timer(interval - (ticks-start-count*interval), hello, [start, interval, count+1])
t.start()
print "Number of ticks since 12:00am, January 1, 1970:", ticks, " #", count
dt = 1.25 # interval in sec
t = Timer(dt, hello, [round(time.time()), dt, 0]) # start over at full second, round only for testing here
t.start()
先记录下开始的时间。然后发送消息。接着记录结束的时间。计算一下花费的时间,也就是结束时间减去开始时间。把这个时间转换成浮点数的秒数。然后让程序暂停一小会儿,时间是0.1秒减去刚才计算的时间。最后再回到最开始的地方,继续这个过程。
是的,简单的等待方式很麻烦,还有更好的选择。
首先,你需要在Python中使用一个高精度的计时器。有几种选择,具体选择哪个取决于你的操作系统,你可能想要选择最准确的那个。
其次,你需要了解基本的抢占式多任务处理,明白没有高精度的sleep
函数,而且它的实际精度在不同的操作系统上也会有所不同。例如,在Windows上,最小的睡眠间隔可能在10到13毫秒之间。
第三,记住总是可以等待一个非常精确的时间间隔(前提是你有一个高分辨率的计时器),但这样会导致CPU负载很高。这种技术叫做忙等待:
while(True):
if time.clock() == something:
break
所以,实际的解决方案是创建一个混合计时器。它会使用常规的sleep
函数来等待大部分时间间隔,然后在循环中开始探测高精度计时器,同时使用sleep(0)
的技巧。Sleep(0)
会(根据平台的不同)等待尽可能短的时间,把剩下的时间片释放给其他进程,并切换CPU上下文。这里有一个相关的讨论。
这个想法在Ryan Geiss的Win32中的计时文章中有详细描述。虽然是用C语言写的,针对Windows API,但基本原理在这里同样适用。