python 实现 延时队列
# 延时队列
import threading
import functools
import queue
import datetime
def test():
print("执行成功")
def seconds_chagne(dt):
return dt.seconds + dt.days * 24 * 60 * 60
# 延迟任务类
class DelayTask:
# delay_time 即将过期的时间
# task 参与延迟的任务
def __init__(self,delay_time,job_func):
self.delay_time = delay_time
self.job_func = job_func
# 延迟队列
class DelayQueue(queue.PriorityQueue):
def __init__(self):
self.can_done = threading.Condition()
# Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法。
# 首先acquire一个条件变量,然后判断一些条件。
# 如果条件不满足则wait;
# 如果条件满足,进行一些处理改变条件后,通过notify方法通知其他线程,其他处于wait状态的线程接到通知后会重新判断条件。
# 不断的重复这一过程,从而解决复杂的同步问题
super(DelayQueue, self).__init__()
# 入队操作
def task_put(self,task):
# 存入元组 (任务的过期时间,当前任务)
self.put((task.delay_time,task))
# 获取队顶数据
def _peek(self):
self.not_empty.acquire()
try:
# 判断队列是否为空
while not self._qsize():
# 为空就等待
self.not_empty.wait()
return self.queue[0][1]
finally:
self.not_empty.release()
# 出队
def get_task(self):
# 获取锁
self.can_done.acquire()
try:
# 获取队顶数据
task = self._peek()
# 判断是否到达过期时间(延迟时间)
delta = seconds_chagne(task.delay_time - datetime.datetime.now())
while delta > 0:
# 等待
self.can_done.acquire(delta)
# 获取任务
task = self._peek()
delta = seconds_chagne(task.delay_time - datetime.datetime.now())
# 出队
item = self.get()
self.can_done.notify_all()
return item[1]
finally:
# 释放锁
self.can_done.release()
# 声明装饰任务
task = functools.partial(test)
# 声明延时任务
task_delay = DelayTask(delay_time=datetime.datetime.now()+datetime.timedelta(seconds=3),job_func=task)
# 实例化队列
delay_queue = DelayQueue()
# 入队
delay_queue.task_put(task_delay)
# 出队
res = delay_queue.get_task()
# 执行方法
res.job_func()
基于redis写的延时队列(有序集合)
import redis
class DelayRedisQueue:
def __init__(self,key):
self.key = key
self.r = redis.Redis(decode_responses=True)
# 入队
def add(self,uid,delay_time=0):
print("延时队列入队,%s秒后执行删除uid%s的任务" % (delay_time,uid))
self.r.zadd(self.key,{uid:time.time()+delay_time})
# 删除延时任务
def remove(self,uid):
return self.r.zrem(self.key,uid)
# 出队
def pop(self):
# 起始位置
min_score = 0
# 区间结束位置
max_score = time.time()
# 获取队列
res = self.r.zrangebyscore(self.key,min_score,max_score,start=0,num=1,withscores=False)
if res == None:
print("暂无延时任务")
return False
if len(res) == 1:
print("延时任务到期,返回执行任务的uid%s"% res[0])
return res[0]
else:
print("延时任务没有到时间")
return False