Go 实现Socket定时器

1.前言

先说说为什么打算写一个定时器,Socket框架需要定时完成一些任务,比如发送心跳包或者定时向用户推送消息,接下来将简单实现多协程定时器单协程轮询定时器。

2.实现步骤

1.初始化并返回定时器

var timerStruct = util.NewTimer()
  1. 注册定时器
// 第一个参数:方法名称,第二个参数间隔多少秒执行一次,后面参数为方法需要传递的参数
timerStruct.RegisterTimer(cron1, "1s", 2,3,4, "tuzisir")
timerStruct.RegisterTimer(cron2, "3s")

3.执行定时器

    timerStruct.ExecTimer()

3.代码示例

定时器核心代码

package util

import (
    "time"
    "reflect"
    "log"
    "strconv"
)

var timeUnit = []string{"h","s"}

// 方法
type funcInfoStruce struct {
    Func interface{}
    FuncTime int
    FuncParams []interface{}
    TimeUnit string
}

// 定时器结构
type TimerStruct struct {
    RegisterFuncs map[string]*funcInfoStruce
    WaitTimerFinsh chan struct{} // 测试使用
    TimerNum int8 // 定时器数量充当map的键值
}

// 初始化返回定时器结构
func NewTimer() *TimerStruct {
    return &TimerStruct{
        RegisterFuncs:make(map[string]*funcInfoStruce),
        WaitTimerFinsh:make(chan struct{}),
        TimerNum:0,
    }
}

// 注册定时器
func (t *TimerStruct) RegisterTimer(registerFunc interface{}, timeFormat string, params ... interface{}) bool {
    // 判断时间格式
    if len([]rune(timeFormat)) == 0 {
        log.Print("时间格式没有输入")
        return false
    }
    // 截取出左右两部分,左数值,右单位
    lTimeFormat := timeFormat[0 : len(timeFormat)-1]
    time,error := strconv.Atoi(lTimeFormat)
    if error != nil{
        log.Println("字符串转换成整数失败")
    }
    // 判断是否为数值
    if !(time > 0) {
        log.Print("不合法时间值")
        return false
    }
    rTimeFormat := timeFormat[len(timeFormat)-1 : len(timeFormat)]
    var issetFormat = false;
    for _, v := range timeUnit {
        if v == rTimeFormat {
            issetFormat = true
            break
        }
    }
    if !issetFormat {
        log.Print("使用默认时间格式s")
        rTimeFormat = "s"
    }
    log.Print(time)
    t.TimerNum++
    fInfo := &funcInfoStruce{
        Func:registerFunc,
        FuncTime:time,
        FuncParams:params,
        TimeUnit:rTimeFormat,
    }
    t.RegisterFuncs[string(t.TimerNum)] = fInfo
    return true
}


// 执行定时器
func (t *TimerStruct) ExecTimer()  {
    t.WaitTimerFinsh = make(chan struct{})
    go func() {
        t.rangeTimer()
    }()
}

// 遍历定时器
func (t *TimerStruct) rangeTimer()  {
    // 遍历出注册的定时器方法
    for _, v := range t.RegisterFuncs {
        go t.execFunc(v)
    }
}

// 执行方法
func (t *TimerStruct) execFunc(v *funcInfoStruce) {
    var unit time.Duration
    if v.TimeUnit == "h" {
        unit = time.Hour
    } else {
        unit = time.Second
    }
    timerTool := time.NewTicker(time.Duration(v.FuncTime) * unit)
    for {
        select {
        case <-timerTool.C:
            f := reflect.ValueOf(v.Func)
            in := make([]reflect.Value, len(v.FuncParams))
            // 将方法参数拼装出来
            for k, param := range v.FuncParams {
                in[k] = reflect.ValueOf(param)
            }
            f.Call(in)
        }
    }
}

测试代码

package main

import (
    "microSocket-master/util"
    "log"
)

var timerStruct = util.NewTimer()

func cron1(a,b,c int, d string)  {
    log.Print(a,b,c,d)
}

func cron2() {
    log.Printf("定时器2\r\n")
}

func main()  {
    log.Printf("1\r\n")
    timerStruct.RegisterTimer(cron1, "1s", 2,3,4, "tuzisir")
    timerStruct.RegisterTimer(cron2, "3s")
    log.Printf("8\r\n")
    timerStruct.ExecTimer()
    // 测试使用,等待所有线程退出,测试代码永不退出
    <-timerStruct.WaitTimerFinsh
    log.Print("2")
}


  1. 执行效果


    image.png

4.单协程轮询定时器

package main

import (
    "time"
    "log"
)

// 定时器任务结构体
type task struct {
    funcs func() bool // 方法
    time_dis int // 间隔多长时间执行
    time_next int // 下一次需要执行的时间
}

// 定时器结构体
type timers struct {
    tasks []*task
    is_close chan bool
}

// 初始化返回定时器对象
func NewTimers() *timers{
    return &timers{
        tasks: make([]*task,0),
        is_close: make(chan bool),
    }
}

// 添加任务
func (this *timers) addTask (time_dis int, funcs func ()bool){
    data := &task{
        funcs:funcs,
        time_dis:time_dis,
        time_next:int(time.Now().Unix()) + time_dis,
    }
    this.tasks = append(this.tasks,data)
}

// 轮询查看需要执行的任务
func (this *timers)do(){
    for _,v := range this.tasks{
        if int64(v.time_next) < time.Now().Unix() {
            v.time_next += v.time_dis
            v.funcs()
        }
    }
}

// 执行定时器
func (this *timers) run(){
    tick := time.NewTicker(1 * time.Second)
    for {
        select {
        case <- this.is_close:
            break
        case <- tick.C:
            this.do()
        }
    }
}

// 测试代码
func main(){
    myTimer := NewTimers()
    myTimer.addTask(2,func()bool{
        log.Println(22222)
        return true
    })

    myTimer.addTask(1,func()bool{
        log.Println(11111)
        return true
    })


    myTimer.run()
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容