Python定时器的一个实现
'''
Created on 2012-10-11
@author: blh
'''
import threading
import time
class PyTimer:
PRECISION = 10 # 10ms
def __init__(self, scheduler_num=1):
self.lock = threading.Lock()
self.schedulers = []
self.scheduler_index = 0
self.scheduler_num = scheduler_num
def get_scheduler(self):
self.lock.acquire()
index = self.scheduler_index
self.scheduler_index = (self.scheduler_index + 1) % self.scheduler_num
self.lock.release()
return self.schedulers[index]
def create(self, callback):
return self.Timer(self, callback)
def start(self):
n = 0
while n < self.scheduler_num:
scheduler = self.Scheduler(n)
scheduler.start()
self.schedulers.append(scheduler)
n += 1
def stop(self):
for scheduler in self.schedulers:
scheduler.stop()
def get_info(self):
info = ''
total = 0
n = 1
for scheduler in self.schedulers:
count = scheduler.get_count()
info += 'timer-{0}: {1}\n'.format(n, count)
total += count
n += 1
info += 'timer total: {0}\n'.format(total)
return info
class Timer:
def __init__(self, manager, callback):
self.manager = manager
self.callback = callback
self.scheduler = None
self.timeout = True
#intval unit is ms
def set(self, intval):
self.intval = int(intval / PyTimer.PRECISION)
self.scheduler = self.manager.get_scheduler()
self.scheduler.set(self)
def cancel(self):
if self.scheduler:
self.scheduler.cancel(self)
def reset(self, intval):
self.cancel()
self.set(intval)
class Scheduler(threading.Thread):
QUEUE_NUM = 300
def __init__(self, scheduler_id):
threading.Thread.__init__(self)
self.scheduler_id = scheduler_id
self.lock = threading.Lock()
self.queues = {}
self.timers = {}
self.current = 0
self.running = True
for n in range(self.QUEUE_NUM):
self.queues[n] = []
def get_offset(self, intval):
offset = intval
if offset >= self.QUEUE_NUM:
offset = (self.QUEUE_NUM - 1)
return offset
def get_queue(self, offset):
qid = (self.current + offset) % self.QUEUE_NUM
return self.queues[qid]
def enqueue(self, timer):
offset = self.get_offset(timer.intval)
timer.intval -= offset
queue = self.get_queue(offset)
self.timers[timer] = queue
queue.append(timer)
def dequeue(self, timer):
if self.timers.has_key(timer):
queue = self.timers.pop(timer)
queue.remove(timer)
def set(self, timer):
assert(timer)
self.lock.acquire()
self.enqueue(timer)
self.lock.release()
return timer
def cancel(self, timer):
self.lock.acquire()
self.dequeue(timer)
self.lock.release()
def stop(self):
self.running = False
def run(self):
base_time = float(PyTimer.PRECISION) / 1000
sleep_time = base_time
while self.running:
if sleep_time > 0.0:
time.sleep(sleep_time)
t1 = time.time()
self.lock.acquire()
queue = self.queues[self.current]
while len(queue):
timer = queue.pop()
if timer.intval:
self.enqueue(timer)
else:
self.timeout(timer)
self.current = (self.current + 1) % self.QUEUE_NUM
self.lock.release()
t2 = time.time()
sleep_time = base_time - (t2 - t1)
def timeout(self, timer):
self.timers.pop(timer)
self.lock.release()
timer.callback()
self.lock.acquire()
def get_count(self):
self.lock.acquire()
count = len(self.timers)
self.lock.release()
return count
class TimerDemo:
def __init__(self):
self.timer = PyTimer()
self.timer.start()
def test(self):
self.t1 = self.timer.create(self.timeout1s)
self.t1.set(1000)
self.t2 = self.timer.create(self.timeout3s)
self.t2.set(3000)
self.t3 = self.timer.create(self.timeout600ms)
self.t3.set(600)
def timeout1s(self):
print 'timeout1s'
self.t1.set(1000)
def timeout3s(self):
print 'timeout3s'
self.t2.set(3000)
def timeout600ms(self):
print 'timeout600ms'
self.t3.set(600)
if __name__ == '__main__':
demo = TimerDemo()
demo.test()