基于close channel广播机制来实现TimingWheel

基于1个Ticker+1个环形数组+多个channel,实现了多个任务在指定最大超时时间范围内的一次性超时通知机制。

代码

package main

import (
	"fmt"
	"sync"
	"time"
)

type TimingWheel struct {
	sync.Mutex

	interval time.Duration

	ticker *time.Ticker
	quit   chan struct{}

	maxTimeout time.Duration

	cs []chan struct{}

	pos int
}

func NewTimingWheel(interval time.Duration, buckets int) *TimingWheel {
	w := new(TimingWheel)

	w.interval = interval

	w.quit = make(chan struct{})
	w.pos = 0

	w.maxTimeout = interval * (time.Duration(buckets))

	// 初始化buckets个非缓冲channel
	w.cs = make([]chan struct{}, buckets)
	for i := range w.cs {
		w.cs[i] = make(chan struct{})
	}

	w.ticker = time.NewTicker(interval)
	go w.run()

	return w
}

func (w *TimingWheel) Stop() {
	close(w.quit)
}

func (w *TimingWheel) After(timeout time.Duration) <-chan struct{} {
	// 不允许timeout>=非缓冲通道数组长度
	if timeout >= w.maxTimeout {
		panic("timeout too much, over maxtimeout")
	}

	// 3/1=3,返回下标是2的非缓冲通道
	index := int(timeout / w.interval)
	if index > 0 {
		index--
	}

	// 在环形数组中根据pos来确定新下标
	w.Lock()
	index = (w.pos + index) % len(w.cs)
	b := w.cs[index]
	w.Unlock()

	return b
}

func (w *TimingWheel) run() {
	for {
		select {
		case <-w.ticker.C:
			w.onTicker()
		case <-w.quit:
			w.ticker.Stop()
			return
		}
	}
}

// 关闭老channel来触发广播,创建新channel
func (w *TimingWheel) onTicker() {
	w.Lock()

	lastCh := w.cs[w.pos]
	w.cs[w.pos] = make(chan struct{})

	w.pos = (w.pos + 1) % len(w.cs)

	w.Unlock()

	close(lastCh)
}

func main() {
	// 最大超时时间是10s
	w := NewTimingWheel(time.Second, 5)
	fmt.Println("start time is ", time.Unix(time.Now().Unix(), 0))

	// 单协程场景
	select {
	// 因为Ticker按照interval来触发,所以等待时间必须是间隔时间的整数倍
	case <-w.After(3 * time.Second):
		fmt.Println("end time is ", time.Unix(time.Now().Unix(), 0))
	}

	// 多协程场景
	for i := 0; i < 3; i++ {
		go func() {
			select {
			case <-w.After(3 * time.Second):
				fmt.Println("end time is ", time.Unix(time.Now().Unix(), 0))
			}
		}()
	}

	time.Sleep(5 * time.Second)
}

执行结果

start time is  2022-11-20 20:14:46 +0800 CST
end time is  2022-11-20 20:14:49 +0800 CST
end time is  2022-11-20 20:14:52 +0800 CST
end time is  2022-11-20 20:14:52 +0800 CST
end time is  2022-11-20 20:14:52 +0800 CST

参考资料

https://studygolang.com/articles/1421/comment/1948

posted on 2022-11-20 20:33  王景迁  阅读(31)  评论(0编辑  收藏  举报

导航