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. 执行效果


    Go 实现Socket定时器_第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()
}

你可能感兴趣的:(Go 实现Socket定时器)