相關文章
在遊戲開發中,很多場景會用到時間間隔觸發的情況。例如:
1、超時處理
2、循環觸發某些處理
3、延後處理等
下面的代碼是一個完整的觸發器應用代碼,通過
New_tickTimerLink()創建一個任務管理器
Add函數用於添加一個延遲任務,包括(延遲時間,次數,任務接口實現)
關鍵點:
1、任務需要實現TimeJob接口
2、Remove函數需要自己補充實現,restart函數留做思考(例子中並沒有真的實現restart)
3、任何以服務形式存在的組件(這裏指tickTimerLink),在設計時需要保證服務不宕機。所以在觸發處理時,使用go關鍵字執行。(go關鍵字後的函數體如出現panic不會導致服務報錯)
4、對定時器的集中管理和鏈式處理(每次新增或執行,只需要關注鏈表頭並處理)
5、細緻觀察會發現,採用的簡單粗暴的隊尾開始對比插入的辦法,之所以這樣做,是因爲在遊戲開發中,延遲需要一般會大量出現類似時間的情況。我們可以在服務器初始化過程中,New_tickTimerLink多次,並將他們分類到不同的需求中(或直接按照延遲時間進行分組)
package timer
import (
"sync"
"sync/atomic"
"time"
)
//時間任務接口
type TimeJob interface {
RunTimeJob() //執行任務
}
type TickTimer struct {
Id uint64 //自己的id
Durt time.Duration //間隔時間
Times int //次數 <=0無限循環
target TimeJob //任務
st int64 //執行時的服務器時間
pre *TickTimer //上一個
next *TickTimer //下一個
group *tickTimerLink //所屬定時器
remain int //剩餘次數
}
//觸發器定時器,可自行構建與釋放
//推薦每個地圖至少擁有一個自己的TickTimerLink已減少過多定時器之間的競爭
//技能等相關不要使用定時器(非公共部分,用時間做驗證即可)
/*
一組排序的tick
每個tickTimerLink擁有自己的id自增
*/
type tickTimerLink struct {
idInc uint64 //自增組id
stopChan chan bool //關閉用的定時器
sync.Mutex //同步鎖
Head *TickTimer //頭
Tail *TickTimer //尾
}
//@return nil
func New_tickTimerLink() *tickTimerLink {
link := &tickTimerLink{}
link.stopChan = make(chan bool)
link.run()
return link
}
//添加一個定時任務
//@return nil
func (this *tickTimerLink) Add(t time.Duration, times int, job TimeJob) *TickTimer {
tid := atomic.AddUint64(&this.idInc, 1)
stime := time.Now().UnixNano()
tick := &TickTimer{
Id: tid,
Durt: t,
Times: times,
target: job,
st: stime + int64(t),
group: this,
}
if tick.Times <= 0 {
tick.remain = -1
} else {
tick.remain = tick.Times
}
this.Lock()
if this.insert(tick) {
this.Unlock()
this.restart()
return tick
}
this.Unlock()
return tick
}
//添加一個定時器
//@return false
func (this *tickTimerLink) insert(t *TickTimer) bool {
t.pre, t.next = nil, nil
if this.Tail == nil { //空隊列
this.Tail = t
this.Head = t
return true
}
now := this.Tail
for now != nil && now.st > t.st {
now = now.pre
}
if now == nil { //插入到表頭
this.Head.pre = t
t.next = this.Head
this.Head = t
return true
} else {
if now == this.Tail { //插入到尾
t.pre = now
now.next = t
this.Tail = t
} else { //插入到中間
next := now.next
t.pre = now
t.next = next
next.pre = t
now.next = t
}
}
return false
}
//刪除一個定時器
func (this *tickTimerLink) remove(t *TickTimer) {
//TODO
}
//移除一個頭
//@return nil
func (this *tickTimerLink) shift() *TickTimer {
if this.Head == nil {
return nil
}
h := this.Head
this.Head = this.Head.next
if this.Head != nil {
this.Head.pre = nil
} else {
this.Tail = nil
}
return h
}
//停止當前得
func (this *tickTimerLink) restart() {
this.stopChan <- false
}
//重啓定時器
func (this *tickTimerLink) run() {
go func() {
for {
nowTime := time.Now().UnixNano()
watiTime := nowTime
this.Lock()
if this.Head != nil {
watiTime = this.Head.st - nowTime
for watiTime <= 0 { //如果已經超時
tc := this.shift()
if tc.remain < 0 {
tc.st = nowTime + int64(tc.Durt)
this.insert(tc)
} else {
tc.remain--
if tc.remain > 0 {
tc.st = nowTime + int64(tc.Durt)
this.insert(tc)
}
}
go tc.target.RunTimeJob()
if this.Head != nil {
watiTime = this.Head.st - nowTime
} else {
break
}
}
}
if this.Head == nil {
watiTime = nowTime
}
this.Unlock()
ticker := time.NewTicker(time.Duration(watiTime))
select {
case <-ticker.C:
ticker.Stop()
ticker = nil
case flag := <-this.stopChan:
ticker.Stop()
ticker = nil
if flag {
return
}
}
}
}()
}