|
关于什么是时间轮算法,自己百度吧,这里列出我用go语言实现的时间轮算法,已经上线应用,稳定。
package timer
import (
"log"
"sync"
"time"
)
const wheel_cnt uint8 = 5
var element_cnt_per_wheel = [wheel_cnt]uint32{256, 64, 64, 64, 64}
var right_shift_per_wheel = [wheel_cnt]uint32{8, 6, 6, 6, 6}
var base_per_wheel = [wheel_cnt]uint32{1, 256, 256 * 64, 256 * 64 * 64, 256 * 64 * 64 * 64}
var mutex sync.Mutex
var rwmutex sync.RWMutex
var newest [wheel_cnt]uint32
var timewheels [5][]*Node
var TimerMap map[string]*Node = make(map[string]*Node)
type Timer struct {
Name string
Inteval uint32
DoSomething func(interface{})
Args interface{}
}
func SetTimer(name string, inteval uint32, handler func(interface{}), args interface{}) {
if inteval <= 0 {
return
}
var bucket_no uint8 = 0
var offset uint32 = inteval
var left uint32 = inteval
for offset >= element_cnt_per_wheel[bucket_no] {
offset >>= right_shift_per_wheel[bucket_no]
var tmp uint32 = 1
if bucket_no == 0 {
tmp = 0
}
left -= base_per_wheel[bucket_no] * (element_cnt_per_wheel[bucket_no] - newest[bucket_no] - tmp)
bucket_no++
}
if offset < 1 {
return
}
if inteval < base_per_wheel[bucket_no]*offset {
return
}
left -= base_per_wheel[bucket_no] * (offset - 1)
pos := (newest[bucket_no] + offset) % element_cnt_per_wheel[bucket_no]
var node Node
node.SetData(Timer{name, left, handler, args})
rwmutex.RLock()
TimerMap[name] = timewheels[bucket_no][pos].InsertHead(node)
rwmutex.RUnlock()
}
func step() {
{
rwmutex.RLock()
var bucket_no uint8 = 0
for bucket_no = 0; bucket_no < wheel_cnt; bucket_no++ {
newest[bucket_no] = (newest[bucket_no] + 1) % element_cnt_per_wheel[bucket_no]
var head *Node = timewheels[bucket_no][newest[bucket_no]]
var firstElement *Node = head.Next()
for firstElement != nil {
if value, ok := firstElement.Data().(Timer); ok {
inteval := value.Inteval
doSomething := value.DoSomething
args := value.Args
if nil != doSomething {
if 0 == bucket_no || 0 == inteval {
go doSomething(args)
} else {
SetTimer(value.Name, inteval, doSomething, args)
}
}
Delete(firstElement)
}
firstElement = head.Next()
}
if 0 != newest[bucket_no] {
break
}
}
rwmutex.RUnlock()
}
}
func Run() {
var i int = 0
for {
go step()
i++
log.Printf("第%ds", i)
time.Sleep(1 * time.Second)
}
}
func init() {
var bucket_no uint8 = 0
for bucket_no = 0; bucket_no < wheel_cnt; bucket_no++ {
var i uint32 = 0
for ; i < element_cnt_per_wheel[bucket_no]; i++ {
timewheels[bucket_no] = append(timewheels[bucket_no], new(Node))
}
}
}
测试下效果
package main
import (
"log"
"runtime"
"timer_server/timer"
)
func callback1(args interface{}) {
if values, ok := args.([]string); ok {
var str1 string = values[0]
var str2 string = values[1]
log.Println("callback1(" + str1 + "," + str2 + ")")
} else {
log.Println("callback1()")
}
}
func callback2(args interface{}) {
timer.SetTimer("callback2", 5, callback2, args)
log.Println("callback2")
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
timer.SetTimer("callback1", 3, callback1, []string{"hello", "world"})
timer.SetTimer("callback2", 6, callback2, nil)
timer.Run()
}
完整的代码在,https://github.com/liberalman/timer_server,这里只列出主要的部分。
将代码下载到您本地GOPATH路径下,进入timer_server目录下,执行
go build
用以编译生成timer_server,没问题的话,运行
./timer_server
看到结果
2015/04/13 13:06:43 第1s
2015/04/13 13:06:44 第2s
2015/04/13 13:06:45 第3s
2015/04/13 13:06:45 callback1(hello,world)
2015/04/13 13:06:46 第4s
2015/04/13 13:06:47 第5s
2015/04/13 13:06:48 第6s
2015/04/13 13:06:48 callback2
2015/04/13 13:06:49 第7s
2015/04/13 13:06:50 第8s
2015/04/13 13:06:51 第9s
2015/04/13 13:06:52 第10s
2015/04/13 13:06:53 第11s
2015/04/13 13:06:53 callback2
……
结果显示第3秒的时候,调用了callback1回调函数,并且只调用了一次,以后不再调用了。
在第6s的时候,调用callback2,并且以后每隔5秒都会调用该函数,成了周期。
然后我们将
这行的注释去掉,看看运行结果
2015/04/13 13:09:31 第1s
2015/04/13 13:09:32 第2s
2015/04/13 13:09:33 第3s
2015/04/13 13:09:34 callback1(hello,world)
2015/04/13 13:09:35 第4s
2015/04/13 13:09:36 第5s
2015/04/13 13:09:37 第6s
2015/04/13 13:09:38 第7s
2015/04/13 13:09:39 第8s
2015/04/13 13:09:40 第9s
2015/04/13 13:09:41 第10s
2015/04/13 13:09:42 第11s
……
可以看到,callback2不再被调用了,因为把这个定时器跟删除了,根据它的名称callback2找到其位置删除的。
始于2015-04-10,北京;更新至2016-06-13,杭州。
|