单线程实现多个定时器
NewTimer.py
#!/usr/bin/env python
from heapq import *
from threading import Timer
import threading
import uuid
import time
import datetime
import sys
import math
global TimerStamp
global TimerTimes
class CancelFail(Exception):
pass
class Slot(object):
def __init__(self,period=0,interval=1,function=None,args=[],kwargs={}):
self.period = period
self.pc = 0
self.interval = interval
self.fire = 0
self.id = uuid.uuid1()
self.function = function
self.args = args
self.kwargs = kwargs
#system resolution millisecond
class NewTimer(object):
#set enough time make thread sleep,when NewTimer empty set enoug time,too
#make sure sum of your timer call back function execute time shorter than resolution
#todo use a worker thread to operate timer call back function
def __init__(self,resolution=1000):
global TimerStamp
TimerStamp = int(time.time() * 1000)
self.nofire = sys.maxint #next fire time interval
self.firestamp = self.nofire + TimerStamp
self.resolution = resolution# 1s
self.lock = threading.RLock()
self.wait = dict()
self.ready = dict()
self._start()
""" private operate ready list """
def _addToReadyList(self,slot,firestamp):
Box = dict( [ (slot.id,slot)])
if not self.ready.has_key( firestamp ):
self.ready.update( [(firestamp,Box)] )
else:
Boxs = self.ready.get(firestamp)
Boxs.update( Box )
def _delFromreadyList(self,slot):
Boxs = self.ready.get(slot.fire)
try:
Box = Boxs.pop(slot.id)
if not Boxs:
self.ready.pop(slot.fire)
except (AttributeError,KeyError):
raise CancelFail
""" inside """
def _start(self):
global TimerStamp
try:
self.firestamp = sorted( self.ready.keys() )[0]
stamp = float((TimerStamp + self.firestamp - int(time.time()*1000)))/1000
except IndexError:
self.firestamp = self.nofire
stamp = self.nofire
try:
self.timer.cancel()
except AttributeError:
pass
self.timer = Timer( stamp,self.hander)
self.timer.start()
def hander(self,*args,**kwargs):
""" find time arrive slot,do it function """
self.lock.acquire()
try:
Boxs = self.ready.pop( self.firestamp )
slots = Boxs.values()
except KeyError:
slots = []
for slot in slots:
if slot.period:
slot.pc += 1
if slot.pc != slot.period:
slot.fire = slot.interval + slot.fire
self._addToReadyList(slot,slot.fire)
elif slot.period == -1:
slot.fire = slot.interval + slot.fire
self._addToReadyList(slot,slot.fire)
""" """
self._start()
self.lock.release()
for slot in slots:
try:
slot.function(slot.args,slot.kwargs)
except Exception:
print "slot id %s,timer function fail" % slot.id
""" operate new timer manager itself """
def stop(self):
self.timer.cancel()
""" new timer manager """
def add(self,kwargs={}):
"""
period: one time = 0,times = >0,always = -1
interval: timer fire relative TimerReference
function: when timer fire,call back function
args,kwargs: callback function args
"""
interval = int(interval) * self.resolution#seconds
if interval < self.resolution:
interval = self.resolution
slot = Slot( period,interval,function,**kwargs )
Box = dict([(slot.id,slot)])
self.wait.update(Box)
return slot
def remove(self,slot):
if isinstance(slot,Slot):
self.cancel(slot)
try:
self.wait.pop(slot.id)
except KeyError:
print "wait dict not has the cancel timer"
""" timer api """
def reset(self,Slot):
self.cancel(slot)
slot.pc = 0
self.start(slot)
def start(self,slot):
def NewTimerStamp(timebase,resolution):
Nowoffset = int(time.time() * 1000) - timebase
if Nowoffset % resolution < resolution / 10:
currentstamp = Nowoffset / resolution
else:
currentstamp = (Nowoffset + resolution - 1) / resolution
return currentstamp * 1000
global TimerStamp
if isinstance(slot,Slot):
firestamp = slot.interval + NewTimerStamp(TimerStamp,self.resolution)
slot.fire = firestamp
self.lock.acquire()
self._addToReadyList(slot,firestamp)
if self.firestamp > slot.fire:
self._start()
self.lock.release()
def cancel(self,Slot):
try:
self.lock.acquire()
self._delFromreadyList(slot)
self._start()
self.lock.release()
except CancelFail:
self.lock.release()
def hello( *args,**kargs):
print args[0],datetime.datetime.Now()
if __name__ == "__main__":
print "start test timer",datetime.datetime.Now()
nt = NewTimer(500)
t0 = nt.add( -1,5,hello,[0])
t1 = nt.add( 4,7,[1])
t2 = nt.add( 1,3,[2])#
t3 = nt.add( 1,4,[3])#
t4 = nt.add( 4,[4])
t5 = nt.add( 12,[5])#
t6 = nt.add( 9,[6])
t7 = nt.add( 1,8,[7])#
t8 = nt.add( 40,1,[8])
nt.start( t0 )
nt.start( t1 )
nt.start( t2 )#
nt.start( t3 )#
nt.start( t4 )
nt.start( t5 )#
nt.start( t6 )
nt.start( t7 )#
nt.start( t8 )
nt.cancel(t2)
nt.cancel(t3)
nt.remove(t5)
nt.remove(t3)
time.sleep(3)
nt.start(t2)
nt.cancel(t8)
time.sleep(300)
nt.stop()
print "finish test timer",datetime.datetime.Now()