当然代码写的很丑,后面会慢慢优化的。
实现步骤
1.结构
- 时间轮的结构是个拉链结构,数组加链表的形式(为了方便任务的添加我这里用了双向链表)。
- 数组是槽位随着时间的增加槽会移动,然后执行槽中链结构,学过java应该知道HashMap就是这样的结构。
- 执行的时候随着时间推移,卡槽会一轮一轮的执行像钟表一样(根据时间差取当前卡槽位置的)
2.实现
1.定义个要执行的接口
// 执行任务的节点
type Node interface {
do() string
}
2.节点定义
// 双向链表
type WheelNode struct {
currentNode *Node
preNode *WheelNode
nextNode *WheelNode
//插入的执行周期 1正常执行 >1周期 0 执行完毕的
circle int64
}
3.定义轮子
// 定义一个轮子
type Wheel struct {
// 时间跨度,单位是毫秒
tick int64
// 时间轮个数
wheelSize int64
//当前时间
currentTime int64
//放槽位位置和轮个数相同
nodes []*WheelNode
}
4.轮子初始化、添加轮子
卡槽的判断可以优化搞成位运算实现,我直接加减乘除了
// 初始时间轮 毫秒
func newWheel(tick int64, wheelSize int64) *Wheel {
nodes := make([]*WheelNode, wheelSize)
for i := range nodes {
nodes[i] = &WheelNode{}
}
now := time.Now()
wheel := &Wheel{
tick: tick,
currentTime: now.UnixNano() / 1e6, //毫秒
wheelSize: wheelSize,
nodes: nodes,
}
return wheel
}
// 添加任务
func (wheel *Wheel) addWheel(later int64, task *Node) bool {
// if task.IsEmpty {
// fmt.Println("执行函数为空!!!")
// return false
// }
if later < wheel.tick {
fmt.Println("延时太小了!!!")
return false
}
now := time.Now().UnixNano() / 1e6
//时间差当前时间+延时-开始时间
defTime := now + later - wheel.currentTime
//一周执行的时间
circle := defTime / (wheel.wheelSize * wheel.tick)
index := defTime % (wheel.wheelSize * wheel.tick) / wheel.tick
addWheel := &WheelNode{
currentNode: task,
circle: 1 + circle,
}
//放尾部
node := wheel.nodes[index]
if node.IsEmpty() {
wheel.nodes[index] = addWheel
addWheel.preNode = addWheel
addWheel.nextNode = addWheel
} else {
addWheel.preNode = node.nextNode
addWheel.nextNode = node
node.preNode.nextNode = addWheel
node.preNode = addWheel
}
fmt.Printf("开始时间:%d,延时时间:%d,周期:%d,插入位置:%d\n", wheel.currentTime, defTime, wheel.nodes[index].preNode.circle, index)
fmt.Println("添加成功!!!")
return true
}
5.轮子执行
// 执行
func (wheel *Wheel) run() {
//当前时间
now := time.Now().UnixNano() / 1e6
defTime := now - wheel.currentTime
//执行位置
index := defTime % (wheel.wheelSize * wheel.tick) / wheel.tick
head, node := wheel.nodes[index], wheel.nodes[index]
fmt.Printf("当前时间:%d,时间差:%d,执行位置:%d\n", now, defTime, index)
for true {
if node.IsEmpty() {
fmt.Println("当前没任务,退出执行")
break
} else {
fmt.Printf("周期:%d\n", node.circle)
var nodeDo Node = *node.currentNode
if node.circle < 1 {
fmt.Println("当前任务已执行完毕")
} else {
nodeDo.do()
node.circle--
}
if head.currentNode == node.preNode.currentNode {
fmt.Println("当前只有一个任务")
break
}
node = node.nextNode
}
}
}
3.本地测试
type timeDo struct {
name string
}
func (t *timeDo) do() string {
fmt.Printf("我是:%s,当前时间打印:%d\n", t.name, time.Now().UnixNano()/1e6)
return "1"
}
func Test1(t *testing.T) {
wheel := newWheel(1000, 60)
var n1, n2, n3 Node
n1 = &timeDo{name: "test"}
wheel.addWheel(1000, &n1)
n2 = &timeDo{name: "汽车"}
wheel.addWheel(63000, &n2)
n3 = &timeDo{name: "火箭"}
wheel.addWheel(3000, &n3)
for i := 0; i < 21; i++ {
time.Sleep(3 * time.Second)
wheel.run()
}
}
欢迎关注公众号:冒泡的肥皂