mirror of
https://gitee.com/johng/gf
synced 2026-07-07 06:15:15 +08:00
timing wheel up
This commit is contained in:
8
TODO.MD
8
TODO.MD
@ -1,8 +1,4 @@
|
||||
# ON THE WAY
|
||||
1. orm增加更多数据库支持;
|
||||
1. 增加对于数据表Model的封装;
|
||||
1. 更多数据库的ORM功能支持;
|
||||
1. 考虑gdb对象管理增加二级连接池特性,提高New&Close性能;
|
||||
1. 增加图形验证码支持,至少支持数字和英文字母;
|
||||
1. 增加热编译工具,提高开发环境的开发/测试效率(媲美PHP开发效率);
|
||||
1. 增加可选择性的orm tag特性,用以数据表记录与struct对象转换的键名属性映射;
|
||||
@ -51,8 +47,8 @@
|
||||
1. glist增加Element类型的并发安全处理;
|
||||
1. 改进证书打开失败时的WebServer错误提示,前置HOOK校验后关闭后续的HOOK逻辑执行;
|
||||
1. 目前WebServer的HOOK是按照优先级执行的,需要增加覆盖特性;
|
||||
|
||||
|
||||
1. 增加jumplist的数据结构容器;
|
||||
1. DelayQueue/PriorityQueue;
|
||||
|
||||
|
||||
# DONE
|
||||
|
||||
43
g/container/garray/garray_z_bench_test.go
Normal file
43
g/container/garray/garray_z_bench_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/garray"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
sortedIntArray = garray.NewSortedIntArray(0)
|
||||
)
|
||||
|
||||
func BenchmarkSortedIntArray_Add(b *testing.B) {
|
||||
b.N = 1000
|
||||
for i := 0; i < b.N; i++ {
|
||||
sortedIntArray.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortedIntArray_Search(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sortedIntArray.Search(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortedIntArray_PopLeft(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sortedIntArray.PopLeft()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortedIntArray_PopRight(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sortedIntArray.PopLeft()
|
||||
}
|
||||
}
|
||||
@ -272,6 +272,16 @@ func (l *List) Remove(e *Element) (value interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 批量删除数据项
|
||||
func (l *List) BatchRemove(es []*Element) {
|
||||
l.mu.Lock()
|
||||
for _, e := range es {
|
||||
l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// 删除所有数据项
|
||||
func (l *List) RemoveAll() {
|
||||
l.mu.Lock()
|
||||
|
||||
@ -4,8 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// Package gring provides a concurrent-safe(alternative) ring(circular lists).
|
||||
// 并发安全的环.
|
||||
// Package gring provides a concurrent-safe(alternative) ring(circular lists)/并发安全的环.
|
||||
package gring
|
||||
|
||||
import (
|
||||
|
||||
@ -20,7 +20,7 @@ const (
|
||||
STATUS_RUNNING = 1
|
||||
STATUS_CLOSED = -1
|
||||
gPANIC_EXIT = "exit"
|
||||
gDEFAULT_TIMES = math.MaxInt64
|
||||
gDEFAULT_TIMES = math.MaxInt32
|
||||
gDEFAULT_SLOT_NUMBER = 10
|
||||
gDEFAULT_WHEEL_INTERVAL = 50*time.Millisecond
|
||||
gDEFAULT_WHEEL_LEVEL = 10
|
||||
@ -28,47 +28,47 @@ const (
|
||||
|
||||
var (
|
||||
// 默认的wheel管理对象
|
||||
defaultWheel = NewDefault()
|
||||
defaultWheels = New(gDEFAULT_SLOT_NUMBER, gDEFAULT_WHEEL_INTERVAL, gDEFAULT_WHEEL_LEVEL)
|
||||
)
|
||||
|
||||
// 添加执行方法,可以给定名字,以便于后续执行删除
|
||||
func Add(interval time.Duration, job JobFunc) *Entry {
|
||||
return defaultWheel.Add(interval, job)
|
||||
return defaultWheels.Add(interval, job)
|
||||
}
|
||||
|
||||
// 添加单例运行循环任务
|
||||
func AddSingleton(interval time.Duration, job JobFunc) *Entry {
|
||||
return defaultWheel.AddSingleton(interval, job)
|
||||
return defaultWheels.AddSingleton(interval, job)
|
||||
}
|
||||
|
||||
// 添加只运行一次的循环任务
|
||||
func AddOnce(interval time.Duration, job JobFunc) *Entry {
|
||||
return defaultWheel.AddOnce(interval, job)
|
||||
return defaultWheels.AddOnce(interval, job)
|
||||
}
|
||||
|
||||
// 添加运行指定次数的循环任务
|
||||
func AddTimes(interval time.Duration, times int, job JobFunc) *Entry {
|
||||
return defaultWheel.AddTimes(interval, times, job)
|
||||
return defaultWheels.AddTimes(interval, times, job)
|
||||
}
|
||||
|
||||
// 延迟添加循环任务,delay参数单位为秒
|
||||
func DelayAdd(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
defaultWheel.DelayAdd(delay, interval, job)
|
||||
defaultWheels.DelayAdd(delay, interval, job)
|
||||
}
|
||||
|
||||
// 延迟添加单例循环任务,delay参数单位为秒
|
||||
func DelayAddSingleton(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
defaultWheel.DelayAddSingleton(delay, interval, job)
|
||||
defaultWheels.DelayAddSingleton(delay, interval, job)
|
||||
}
|
||||
|
||||
// 延迟添加只运行一次的循环任务,delay参数单位为秒
|
||||
func DelayAddOnce(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
defaultWheel.DelayAddOnce(delay, interval, job)
|
||||
defaultWheels.DelayAddOnce(delay, interval, job)
|
||||
}
|
||||
|
||||
// 延迟添加运行指定次数的循环任务,delay参数单位为秒
|
||||
func DelayAddTimes(delay time.Duration, interval time.Duration, times int, job JobFunc) {
|
||||
defaultWheel.DelayAddTimes(delay, interval, times, job)
|
||||
defaultWheels.DelayAddTimes(delay, interval, times, job)
|
||||
}
|
||||
|
||||
// 在Job方法中调用,停止当前运行的任务
|
||||
|
||||
@ -13,42 +13,59 @@ import (
|
||||
|
||||
// 循环任务项
|
||||
type Entry struct {
|
||||
singleton *gtype.Bool // 任务是否单例运行
|
||||
status *gtype.Int // 任务状态(0: ready; 1: running; -1: closed)
|
||||
times *gtype.Int64 // 还需运行次数
|
||||
create int64 // 注册时的时间轮ticks
|
||||
interval int64 // 设置的运行间隔(时间轮刻度数量)
|
||||
job JobFunc // 注册循环任务方法
|
||||
id int64 // ID
|
||||
job JobFunc // 注册循环任务方法
|
||||
wheel *wheel // 所属时间轮
|
||||
singleton *gtype.Bool // 任务是否单例运行
|
||||
status *gtype.Int // 任务状态(0: ready; 1: running; -1: closed)
|
||||
times *gtype.Int // 还需运行次数
|
||||
create int64 // 注册时的时间轮ticks
|
||||
update *gtype.Int64 // 更新时间(上一次检查/运行时间, 毫秒)
|
||||
interval int64 // 设置的运行间隔(时间轮刻度数量)
|
||||
createMs int64 // 创建时间(毫秒)
|
||||
intervalMs int64 // 间隔时间(毫秒)
|
||||
}
|
||||
|
||||
// 任务执行方法
|
||||
type JobFunc func()
|
||||
|
||||
// 创建循环任务
|
||||
// 创建循环任务(失败返回nil)
|
||||
func (w *wheel) newEntry(interval time.Duration, job JobFunc, singleton bool, times int) *Entry {
|
||||
// 安装任务的间隔时间(纳秒)
|
||||
n := interval.Nanoseconds()
|
||||
// 计算出所需的插槽数量
|
||||
num := int(n/w.interval)
|
||||
ims := interval.Nanoseconds()/1e6
|
||||
num := ims/w.intervalMs
|
||||
if num == 0 {
|
||||
// 如果添加的任务间隔时间比时间轮的刻度还小,
|
||||
// 那么默认为1个刻度
|
||||
num = 1
|
||||
return nil
|
||||
}
|
||||
nowMs := time.Now().UnixNano()/1e6
|
||||
ticks := w.ticks.Val()
|
||||
entry := &Entry {
|
||||
singleton : gtype.NewBool(singleton),
|
||||
status : gtype.NewInt(STATUS_READY),
|
||||
times : gtype.NewInt64(int64(times)),
|
||||
job : job,
|
||||
create : ticks,
|
||||
interval : int64(num),
|
||||
id : time.Now().UnixNano(),
|
||||
wheel : w,
|
||||
singleton : gtype.NewBool(singleton),
|
||||
status : gtype.NewInt(STATUS_READY),
|
||||
times : gtype.NewInt(times),
|
||||
job : job,
|
||||
create : ticks,
|
||||
update : gtype.NewInt64(nowMs),
|
||||
interval : num,
|
||||
createMs : nowMs,
|
||||
intervalMs : ims,
|
||||
}
|
||||
// 计算安装的slot数量(可能多个)
|
||||
index := int(ticks%int64(w.number))
|
||||
for i := 0; i < w.number; i += num {
|
||||
w.slots[(i + index + num) % w.number].PushBack(entry)
|
||||
// 安装的slot(可能多个)
|
||||
set := make(map[int64]struct{})
|
||||
index := int64(ticks%int64(w.number))
|
||||
for i, j := int64(0), int64(0); i < int64(times); {
|
||||
t := (i + index + num) % w.number
|
||||
if _, ok := set[t]; ok {
|
||||
break
|
||||
}
|
||||
set[t] = struct{}{}
|
||||
w.slots[t].PushBack(entry)
|
||||
//fmt.Println("addEntry:", w.level, t, ticks, entry.create, time.Now(), entry.id)
|
||||
i += 1
|
||||
j += num
|
||||
}
|
||||
//fmt.Println("addEntry:", w.level, ticks, entry.create, time.Now(), entry.update.Val())
|
||||
return entry
|
||||
}
|
||||
|
||||
@ -79,7 +96,7 @@ func (entry *Entry) SetSingleton(enabled bool) {
|
||||
|
||||
// 设置任务的运行次数
|
||||
func (entry *Entry) SetTimes(times int) {
|
||||
entry.times.Set(int64(times))
|
||||
entry.times.Set(times)
|
||||
}
|
||||
|
||||
// 执行任务
|
||||
@ -88,9 +105,8 @@ func (entry *Entry) Run() {
|
||||
}
|
||||
|
||||
// 检测当前任务是否可运行, 参数为当前时间的纳秒数, 精度更高
|
||||
func (entry *Entry) runnableCheck(ticks int64) bool {
|
||||
diff := ticks - entry.create
|
||||
if diff > 0 && diff%int64(entry.interval) == 0 {
|
||||
func (entry *Entry) runnableCheck(nowTicks int64, nowMs int64) bool {
|
||||
if diff := nowTicks - entry.create; diff > 0 && diff%entry.interval == 0 {
|
||||
// 是否关闭
|
||||
if entry.status.Val() == STATUS_CLOSED {
|
||||
return false
|
||||
@ -102,11 +118,37 @@ func (entry *Entry) runnableCheck(ticks int64) bool {
|
||||
}
|
||||
}
|
||||
// 次数限制
|
||||
if entry.times.Add(-1) < 0 {
|
||||
if entry.status.Set(STATUS_CLOSED) == STATUS_CLOSED {
|
||||
times := entry.times.Add(-1)
|
||||
if times <= 0 {
|
||||
entry.status.Set(STATUS_CLOSED)
|
||||
if times < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// 是否不限制运行次数
|
||||
if times > 2000000000 {
|
||||
times = gDEFAULT_TIMES
|
||||
entry.times.Set(gDEFAULT_TIMES)
|
||||
}
|
||||
// 分层转换
|
||||
if entry.wheel.level > 0 {
|
||||
if diff := nowMs - entry.update.Val(); diff < entry.intervalMs {
|
||||
delay := time.Duration(entry.intervalMs - diff)*time.Millisecond
|
||||
//fmt.Println("LEVEL:", entry.wheel.level, times, delay,
|
||||
// time.Duration(entry.intervalMs)*time.Millisecond, time.Now(),
|
||||
// entry.update.Val(),
|
||||
//)
|
||||
// 往底层添加
|
||||
entry.wheel.wheels.newEntry(delay, entry.job, false, 1, entry.wheel)
|
||||
// 延迟重新添加
|
||||
if times > 0 {
|
||||
entry.wheel.wheels.DelayAddTimes(delay, time.Duration(entry.intervalMs)*time.Millisecond, times, entry.job)
|
||||
}
|
||||
entry.status.Set(STATUS_CLOSED)
|
||||
return false
|
||||
}
|
||||
}
|
||||
entry.update.Set(nowMs)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@ -9,17 +9,20 @@ package gwheel
|
||||
import (
|
||||
"container/list"
|
||||
"gitee.com/johng/gf/g/container/glist"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 开始循环
|
||||
func (w *wheel) start() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Duration(w.intervalMs)*time.Millisecond)
|
||||
for {
|
||||
select {
|
||||
case <- w.closed:
|
||||
ticker.Stop()
|
||||
return
|
||||
|
||||
case <- w.ticker.C:
|
||||
case <- ticker.C:
|
||||
w.proceed()
|
||||
}
|
||||
}
|
||||
@ -29,18 +32,26 @@ func (w *wheel) start() {
|
||||
// 执行时间轮刻度逻辑, 遍历检查可执行循环任务,并异步执行
|
||||
func (w *wheel) proceed() {
|
||||
n := w.ticks.Add(1)
|
||||
l := w.slots[int(n%int64(w.number))]
|
||||
l := w.slots[int(n%w.number)]
|
||||
if l.Len() > 0 {
|
||||
go func(l *glist.List, ticks int64) {
|
||||
go func(l *glist.List, nowTicks int64) {
|
||||
nowMs := time.Now().UnixNano()/1e6
|
||||
removeArray := make([]*glist.Element, 0)
|
||||
l.RLockFunc(func(list *list.List) {
|
||||
for e := list.Front(); e != nil; e = e.Next() {
|
||||
entry := e.Value.(*Entry)
|
||||
// 任务是否已关闭,那么需要删除
|
||||
if entry.Status() == STATUS_CLOSED {
|
||||
removeElement := e
|
||||
removeArray = append(removeArray, removeElement)
|
||||
continue
|
||||
}
|
||||
// 是否满足运行条件
|
||||
if !entry.runnableCheck(ticks) {
|
||||
if !entry.runnableCheck(nowTicks, nowMs) {
|
||||
continue
|
||||
}
|
||||
// 异步执行运行
|
||||
go func(e *glist.Element, l *glist.List) {
|
||||
go func(entry *Entry, e *glist.Element, l *glist.List) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
if err != gPANIC_EXIT {
|
||||
@ -51,18 +62,32 @@ func (w *wheel) proceed() {
|
||||
}
|
||||
switch entry.Status() {
|
||||
case STATUS_CLOSED:
|
||||
//fmt.Println("remove:", w.level, entry.interval)
|
||||
l.Remove(e)
|
||||
|
||||
case STATUS_RUNNING:
|
||||
entry.SetStatus(STATUS_READY)
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
entry.Run()
|
||||
}(e, l)
|
||||
//if entry.interval == 8 {
|
||||
// fmt.Println(w.level, ticks, entry.create, entry.interval,
|
||||
// entry.times.Val(),
|
||||
// entry.Status(),
|
||||
// time.Duration(w.interval),
|
||||
// time.Duration(entry.interval)*time.Duration(w.interval),
|
||||
// time.Now(),
|
||||
// entry.id,
|
||||
// )
|
||||
//}
|
||||
|
||||
entry.job()
|
||||
}(entry, e, l)
|
||||
}
|
||||
})
|
||||
if len(removeArray) > 0 {
|
||||
l.BatchRemove(removeArray)
|
||||
}
|
||||
}(l, n)
|
||||
}
|
||||
}
|
||||
@ -1,107 +0,0 @@
|
||||
// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
package gwheel
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/glist"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 单层时间轮
|
||||
type wheel struct {
|
||||
slots []*glist.List // 所有的循环任务项, 按照Slot Number进行分组
|
||||
number int // Slot Number
|
||||
closed chan struct{} // 停止事件
|
||||
ticks *gtype.Int64 // 当前时间轮已转动的刻度数量
|
||||
ticker *time.Ticker // 时间轮刻度间隔
|
||||
interval int64 // 时间间隔(slot时间长度, 纳秒)
|
||||
}
|
||||
|
||||
|
||||
// 创建使用默认值的时间轮
|
||||
func NewDefault() *wheel {
|
||||
return newWheel(gDEFAULT_SLOT_NUMBER, gDEFAULT_WHEEL_INTERVAL)
|
||||
}
|
||||
|
||||
// 创建自定义的循环任务管理对象
|
||||
func newWheel(slot int, interval time.Duration) *wheel {
|
||||
w := &wheel {
|
||||
slots : make([]*glist.List, slot),
|
||||
number : slot,
|
||||
closed : make(chan struct{}, 1),
|
||||
ticks : gtype.NewInt64(),
|
||||
ticker : time.NewTicker(interval),
|
||||
interval : interval.Nanoseconds(),
|
||||
}
|
||||
for i := 0; i < w.number; i++ {
|
||||
w.slots[i] = glist.New()
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// 添加循环任务
|
||||
func (w *wheel) Add(interval time.Duration, job JobFunc) *Entry {
|
||||
return w.newEntry(interval, job, false, gDEFAULT_TIMES)
|
||||
}
|
||||
|
||||
// 添加单例运行循环任务
|
||||
func (w *wheel) AddSingleton(interval time.Duration, job JobFunc) *Entry {
|
||||
return w.newEntry(interval, job, true, gDEFAULT_TIMES)
|
||||
}
|
||||
|
||||
// 添加只运行一次的循环任务
|
||||
func (w *wheel) AddOnce(interval time.Duration, job JobFunc) *Entry {
|
||||
return w.newEntry(interval, job, false, 1)
|
||||
}
|
||||
|
||||
// 添加运行指定次数的循环任务
|
||||
func (w *wheel) AddTimes(interval time.Duration, times int, job JobFunc) *Entry {
|
||||
return w.newEntry(interval, job, false, times)
|
||||
}
|
||||
|
||||
// 延迟添加循环任务
|
||||
func (w *wheel) DelayAdd(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
w.AddOnce(delay, func() {
|
||||
w.Add(interval, job)
|
||||
})
|
||||
}
|
||||
|
||||
// 延迟添加单例循环任务
|
||||
func (w *wheel) DelayAddSingleton(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
w.AddOnce(delay, func() {
|
||||
w.AddSingleton(interval, job)
|
||||
})
|
||||
}
|
||||
|
||||
// 延迟添加只运行一次的循环任务
|
||||
func (w *wheel) DelayAddOnce(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
w.AddOnce(delay, func() {
|
||||
w.AddOnce(interval, job)
|
||||
})
|
||||
}
|
||||
|
||||
// 延迟添加只运行一次的循环任务
|
||||
func (w *wheel) DelayAddTimes(delay time.Duration, interval time.Duration, times int, job JobFunc) {
|
||||
w.AddOnce(delay, func() {
|
||||
w.AddTimes(interval, times, job)
|
||||
})
|
||||
}
|
||||
|
||||
// 任务数量
|
||||
func (w *wheel) Size() (size int) {
|
||||
for _, l := range w.slots {
|
||||
size += l.Len()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 关闭循环任务
|
||||
func (w *wheel) Close() {
|
||||
w.ticker.Stop()
|
||||
w.closed <- struct{}{}
|
||||
}
|
||||
@ -7,15 +7,17 @@
|
||||
package gwheel
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/glist"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 分层时间轮
|
||||
type Wheels struct {
|
||||
levels []*wheel // 分层
|
||||
length int // 层数
|
||||
number int // 每一层Slot Number
|
||||
interval int64 // 最小时间刻度(纳秒)
|
||||
levels []*wheel // 分层
|
||||
length int // 层数
|
||||
number int // 每一层Slot Number
|
||||
intervalMs int64 // 最小时间刻度(毫秒)
|
||||
}
|
||||
|
||||
// 创建分层时间轮
|
||||
@ -24,64 +26,146 @@ func New(slot int, interval time.Duration, level...int) *Wheels {
|
||||
if len(level) > 0 {
|
||||
length = level[0]
|
||||
}
|
||||
w := &Wheels {
|
||||
levels : make([]*wheel, length),
|
||||
length : length,
|
||||
number : slot,
|
||||
interval : interval.Nanoseconds(),
|
||||
ws := &Wheels {
|
||||
levels : make([]*wheel, length),
|
||||
length : length,
|
||||
number : slot,
|
||||
intervalMs : interval.Nanoseconds()/1e6,
|
||||
}
|
||||
for i := 0; i < length; i++ {
|
||||
if i > 0 {
|
||||
n := interval*time.Duration(slot*i)
|
||||
w.levels[i] = newWheel(slot, n)
|
||||
w.levels[i - 1].Add(n, func() {
|
||||
w.levels[i].proceed()
|
||||
})
|
||||
n := time.Duration(ws.levels[i - 1].totalMs)*time.Millisecond
|
||||
w := ws.newWheel(i, slot, n)
|
||||
ws.levels[i] = w
|
||||
ws.levels[i - 1].newEntry(n, w.proceed, false, gDEFAULT_TIMES)
|
||||
} else {
|
||||
w.levels[i] = newWheel(slot, interval)
|
||||
ws.levels[i] = ws.newWheel(i, slot, interval)
|
||||
}
|
||||
}
|
||||
w.levels[0].start()
|
||||
ws.levels[0].start()
|
||||
return ws
|
||||
}
|
||||
|
||||
// 创建自定义的循环任务管理对象
|
||||
func (ws *Wheels) newWheel(level int, slot int, interval time.Duration) *wheel {
|
||||
w := &wheel {
|
||||
wheels : ws,
|
||||
level : level,
|
||||
slots : make([]*glist.List, slot),
|
||||
number : int64(slot),
|
||||
closed : make(chan struct{}, 1),
|
||||
ticks : gtype.NewInt64(),
|
||||
totalMs : int64(slot)*interval.Nanoseconds()/1e6,
|
||||
createMs : time.Now().UnixNano()/1e6,
|
||||
intervalMs : interval.Nanoseconds()/1e6,
|
||||
}
|
||||
for i := int64(0); i < w.number; i++ {
|
||||
w.slots[i] = glist.New()
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// 添加循环任务
|
||||
func (w *Wheels) Add(interval time.Duration, job JobFunc) *Entry {
|
||||
n := interval.Nanoseconds()
|
||||
pos, cmp := w.binSearchIndex(n)
|
||||
func (ws *Wheels) Add(interval time.Duration, job JobFunc) *Entry {
|
||||
return ws.newEntry(interval, job, false, gDEFAULT_TIMES)
|
||||
}
|
||||
|
||||
// 添加单例运行循环任务
|
||||
func (ws *Wheels) AddSingleton(interval time.Duration, job JobFunc) *Entry {
|
||||
return ws.newEntry(interval, job, true, gDEFAULT_TIMES)
|
||||
}
|
||||
|
||||
// 添加只运行一次的循环任务
|
||||
func (ws *Wheels) AddOnce(interval time.Duration, job JobFunc) *Entry {
|
||||
return ws.newEntry(interval, job, false, 1)
|
||||
}
|
||||
|
||||
// 添加运行指定次数的循环任务
|
||||
func (ws *Wheels) AddTimes(interval time.Duration, times int, job JobFunc) *Entry {
|
||||
return ws.newEntry(interval, job, false, times)
|
||||
}
|
||||
|
||||
// 延迟添加循环任务
|
||||
func (ws *Wheels) DelayAdd(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
ws.AddOnce(delay, func() {
|
||||
ws.Add(interval, job)
|
||||
})
|
||||
}
|
||||
|
||||
// 延迟添加单例循环任务
|
||||
func (ws *Wheels) DelayAddSingleton(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
ws.AddOnce(delay, func() {
|
||||
ws.AddSingleton(interval, job)
|
||||
})
|
||||
}
|
||||
|
||||
// 延迟添加只运行一次的循环任务
|
||||
func (ws *Wheels) DelayAddOnce(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
ws.AddOnce(delay, func() {
|
||||
ws.AddOnce(interval, job)
|
||||
})
|
||||
}
|
||||
|
||||
// 延迟添加只运行一次的循环任务
|
||||
func (ws *Wheels) DelayAddTimes(delay time.Duration, interval time.Duration, times int, job JobFunc) {
|
||||
ws.AddOnce(delay, func() {
|
||||
ws.AddTimes(interval, times, job)
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭分层时间轮
|
||||
func (ws *Wheels) Close() {
|
||||
for _, w := range ws.levels {
|
||||
w.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// 添加循环任务
|
||||
func (ws *Wheels) newEntry(interval time.Duration, job JobFunc, singleton bool, times int, from...*wheel) *Entry {
|
||||
intervalMs := interval.Nanoseconds()/1e6
|
||||
pos, cmp := ws.binSearchIndex(intervalMs)
|
||||
if len(from) > 0 {
|
||||
pos = from[0].level - 1
|
||||
cmp = -1
|
||||
}
|
||||
switch cmp {
|
||||
case -1 :
|
||||
for i := pos; i >= 0; i-- {
|
||||
if n > w.levels[i].interval {
|
||||
return w.levels[i].Add(time.Duration(n), job)
|
||||
// n比最后匹配值小
|
||||
case -1:
|
||||
i := pos
|
||||
for ; i > 0; i-- {
|
||||
if intervalMs > ws.levels[i].totalMs {
|
||||
return ws.levels[i].newEntry(interval, job, singleton, times)
|
||||
}
|
||||
}
|
||||
return w.levels[0].Add(time.Duration(n), job)
|
||||
case 1 :
|
||||
for i := pos; i < w.length; i++ {
|
||||
if n > w.levels[i].interval {
|
||||
return w.levels[i].Add(time.Duration(n), job)
|
||||
return ws.levels[i].newEntry(interval, job, singleton, times)
|
||||
// n比最后匹配值大
|
||||
case 1:
|
||||
i := pos
|
||||
for ; i < ws.length - 1; i++ {
|
||||
if intervalMs < ws.levels[i].totalMs {
|
||||
return ws.levels[i].newEntry(interval, job, singleton, times)
|
||||
}
|
||||
}
|
||||
return w.levels[w.length - 1].Add(time.Duration(n), job)
|
||||
case 0 :
|
||||
return w.levels[pos].Add(time.Duration(n), job)
|
||||
return ws.levels[i].newEntry(interval, job, singleton, times)
|
||||
|
||||
case 0:
|
||||
return ws.levels[pos].newEntry(interval, job, singleton, times)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (w *Wheels) binSearchIndex(n int64)(index int, result int) {
|
||||
func (ws *Wheels) binSearchIndex(n int64)(index int, result int) {
|
||||
min := 0
|
||||
max := w.length - 1
|
||||
max := ws.length - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
switch {
|
||||
case w.levels[mid].interval == n : cmp = 0
|
||||
case w.levels[mid].interval > n : cmp = 1
|
||||
case w.levels[mid].interval < n : cmp = -1
|
||||
case ws.levels[mid].totalMs == n : cmp = 0
|
||||
case ws.levels[mid].totalMs > n : cmp = -1
|
||||
case ws.levels[mid].totalMs < n : cmp = 1
|
||||
}
|
||||
switch cmp {
|
||||
case -1 : max = mid - 1
|
||||
|
||||
30
g/os/gwheel/gwheel_wheels_wheel.go
Normal file
30
g/os/gwheel/gwheel_wheels_wheel.go
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
package gwheel
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/glist"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
)
|
||||
|
||||
// 单层时间轮
|
||||
type wheel struct {
|
||||
wheels *Wheels // 所属分层时间轮
|
||||
level int // 所属分层索引号
|
||||
slots []*glist.List // 所有的循环任务项, 按照Slot Number进行分组
|
||||
number int64 // Slot Number
|
||||
closed chan struct{} // 停止事件
|
||||
ticks *gtype.Int64 // 当前时间轮已转动的刻度数量
|
||||
totalMs int64 // 整个时间轮的时间长度(毫秒)=number*interval
|
||||
createMs int64 // 创建时间(毫秒)
|
||||
intervalMs int64 // 时间间隔(slot时间长度, 毫秒)
|
||||
}
|
||||
|
||||
// 关闭循环任务
|
||||
func (w *wheel) Close() {
|
||||
w.closed <- struct{}{}
|
||||
}
|
||||
@ -16,9 +16,14 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
func New() *gwheel.Wheels {
|
||||
return gwheel.New(10, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestWheel_Add_Close(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
//fmt.Println("start", time.Now())
|
||||
wheel.Add(time.Second, func() {
|
||||
@ -47,7 +52,7 @@ func TestWheel_Add_Close(t *testing.T) {
|
||||
|
||||
func TestWheel_Singleton(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.AddSingleton(time.Second, func() {
|
||||
array.Append(1)
|
||||
@ -63,7 +68,7 @@ func TestWheel_Singleton(t *testing.T) {
|
||||
|
||||
func TestWheel_Once(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.AddOnce(time.Second, func() {
|
||||
array.Append(1)
|
||||
@ -85,7 +90,7 @@ func TestWheel_Once(t *testing.T) {
|
||||
|
||||
func TestWheel_DelayAdd(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.DelayAdd(time.Second, time.Second, func() {
|
||||
array.Append(1)
|
||||
@ -99,7 +104,7 @@ func TestWheel_DelayAdd(t *testing.T) {
|
||||
|
||||
func TestWheel_DelayAdd_Singleton(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.DelayAddSingleton(time.Second, time.Second, func() {
|
||||
array.Append(1)
|
||||
@ -115,7 +120,7 @@ func TestWheel_DelayAdd_Singleton(t *testing.T) {
|
||||
|
||||
func TestWheel_DelayAdd_Once(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.DelayAddOnce(time.Second, time.Second, func() {
|
||||
array.Append(1)
|
||||
@ -133,7 +138,7 @@ func TestWheel_DelayAdd_Once(t *testing.T) {
|
||||
|
||||
func TestWheel_ExitJob(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.Add(time.Second, func() {
|
||||
array.Append(1)
|
||||
|
||||
@ -10,14 +10,13 @@ package gwheel_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/garray"
|
||||
"gitee.com/johng/gf/g/os/gwheel"
|
||||
"gitee.com/johng/gf/g/util/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWheel_Entry_Operation(t *testing.T) {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
entry := wheel.Add(time.Second, func() {
|
||||
array.Append(1)
|
||||
@ -30,7 +29,7 @@ func TestWheel_Entry_Operation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWheel_Entry_Singleton(t *testing.T) {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
entry := wheel.Add(time.Second, func() {
|
||||
array.Append(1)
|
||||
@ -45,7 +44,7 @@ func TestWheel_Entry_Singleton(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWheel_Entry_Once(t *testing.T) {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
entry := wheel.Add(time.Second, func() {
|
||||
array.Append(1)
|
||||
|
||||
@ -10,7 +10,6 @@ package gwheel_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/garray"
|
||||
"gitee.com/johng/gf/g/os/gwheel"
|
||||
"gitee.com/johng/gf/g/util/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
@ -18,7 +17,7 @@ import (
|
||||
|
||||
func TestWheel_Times(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := gwheel.NewDefault()
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.AddTimes(time.Second, 2, func() {
|
||||
array.Append(1)
|
||||
|
||||
@ -7,9 +7,10 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
_, err := gwheel.Add(time.Second, func() {
|
||||
fmt.Println("START:", time.Now())
|
||||
gwheel.Add(1400*time.Millisecond, func() {
|
||||
fmt.Println(time.Now())
|
||||
})
|
||||
fmt.Println(err)
|
||||
|
||||
select { }
|
||||
}
|
||||
|
||||
@ -1,26 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"gitee.com/johng/gf/g/os/glog"
|
||||
"gitee.com/johng/gf/g/os/gwheel"
|
||||
"time"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
v := gtype.NewInt()
|
||||
w := gwheel.New(1, 10*time.Millisecond)
|
||||
glog.Println("start")
|
||||
for i := 0; i < 100000; i++ {
|
||||
w.AddOnce(time.Second, func() {
|
||||
v.Add(1)
|
||||
})
|
||||
}
|
||||
glog.Println("end")
|
||||
time.Sleep(1020*time.Millisecond)
|
||||
glog.Println(v.Val())
|
||||
//gwheel.AddSingleton(time.Second, func() {
|
||||
// fmt.Println(time.Now().String())
|
||||
//})
|
||||
//select { }
|
||||
i := int64(5)
|
||||
n := int64(3)
|
||||
m := i/n
|
||||
fmt.Println(m)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user