go学习写个简单的时间轮

142 阅读2分钟

当然代码写的很丑,后面会慢慢优化的。

实现步骤

1.结构

  • 时间轮的结构是个拉链结构,数组加链表的形式(为了方便任务的添加我这里用了双向链表)。
  • 数组是槽位随着时间的增加槽会移动,然后执行槽中链结构,学过java应该知道HashMap就是这样的结构。
  • 执行的时候随着时间推移,卡槽会一轮一轮的执行像钟表一样(根据时间差取当前卡槽位置的)

image.png

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()
	}
}

image.png

欢迎关注公众号:冒泡的肥皂

image.png