• golang 循环执行定时任务


    有时我们想在自己的服务里单独弄一个定时器,但是又不想让定时器的定时任务成为主线程,而是作为 http 服务或者 rpc 服务的一个子线程来执行任务。

    一、定时器 NewTicker

    1、第一种写法

    package main
    
    import (
        "time"
        "fmt"
    )
    
    func printDemo() {
      fmt.Println("demo........")
    }
    
    
    // 初始化 demo 定时器
    func InitDemoScheduler()  {
        // 每 5 秒钟时执行一次
        ticker := time.NewTicker(5 * time.Second) // 创建一个定时器
        go func() { // 用新协程去执行定时任务
            defer func() {
    		if r := recover(); r != nil {
    			logs.Error("定时器发生错误,%v", r)
    		}
    		ticker.Stop() // 意外退出时关闭定时器
    	}()
            printDemo()   // 协程启动时启动一次,之后每 5 秒执行一次,如果没有这行,只有等到协程启动后的第 5 秒才会第一次执行任务
            for  { // 用上一个死循环,不停地执行,否则只会执行一次
                select { 
                    case <- ticker.C: // 时间到了就会触发这个分支的执行,其实时间到了定时器会往ticker.C 这个 channel 中写一条数据,随后被 select 捕捉到channel中有数据可读,就读取channel数据,执行相应分支的语句
                        printDemo()   
                }
            }
        }()
    }
    
    func main(){
    
      // 初始化定时器,每 5s 会打印一个「demo........」
      InitDemoScheduler()
      
      // 等待,避免主线程退出,实际应用时这里可以时启动 http 服务器的监听动作,或者启动 rpc 服务的监听动作,所以不需要 sleep
      time.sleep(100*time.Second)
    }
    
    

    case <- ticker.C: 间到了定时器会往ticker.C 这个 channel 中写一条数据,随后被 select 捕捉到 channel 中有数据可读,就读取 channel 数据,并执行相应分支的语句,select 语法可以参考:golang中的select详解(转)

    还有个需要注意的点是:如果需要在协程启动时执行一次任务,需要在在 for 循环之前额外执行一次,这样在协程启动时会启动一次,之后每 5 秒执行一次,如果没有这行,只有等到协程启动后的第 5 秒才会第一次执行任务


    2、另一种写法


    发现一种新的写法,这种写法没有用 select 语法,而是通过 for 循环从 channel 中取数,理论上应该也可以。但是没实验过,不知道行不行,

    // 初始化 demo 定时器
    func InitDemoScheduler()  {
    	// 每 5 秒钟时执行一次
    	ticker := time.NewTicker(5 * time.Second) // 创建一个定时器
    	go func() { // 用新协程去执行定时任务
    		defer func() {
    			if r := recover(); r != nil {
    				logs.Error("定时器发生错误,%v", r)
    			}
    			ticker.Stop() // 意外退出时关闭定时器
    		}()
                    printDemo()   // 协程启动时启动一次,之后每 5 秒执行一次,如果没有这行,只有等到协程启动后的第 5 秒才会第一次执行任务
    		for _ = range ticker.C {
    			printDemo()
    		}
    	}()
    }
    

    3、可随时退出的定时任务


    上面的两种写法,定时器在创建完成后,协程永远无法退出,如果你想提前退出,可以使用一个无缓冲泳道,对外提供一个往该泳道发送消息的函数,定时任务进程在监听定时器的同时也监听这个无缓冲泳道,如果监听到无缓冲泳道的消息,则立刻 return 终止协程,也就终止了定时任务。

    package main
    
    import (
        "time"
        "fmt"
    )
    
    func printDemo() {
      fmt.Println("demo........")
    }
    
    // 一个无缓冲泳道
    var stopFlag = make(chan bool)
    
    // 对外提供一个往泳道写消息的函数,如果想关闭定时任务,调用该函数即可。
    func CloseDemoScheduler()  {
    	stopFlag <- false
    }
    
    
    // 初始化 demo 定时器
    func InitDemoScheduler()  {
    	// 每 5 秒钟时执行一次
    	ticker := time.NewTicker(5 * time.Second) // 创建一个定时器
    	go func() { // 用新协程去执行定时任务
    		defer func() {
    			if r := recover(); r != nil {
    				logs.Error("定时器发生错误,%v", r)
    			}
    			ticker.Stop() // 意外退出时关闭定时器
    		}()
    		printDemo()   // 协程启动时启动一次,之后每 5 秒执行一次,如果没有这行,只有等到协程启动后的第 5 秒才会第一次执行任务
    		for  { // 用上一个死循环,不停地执行,否则只会执行一次
    			select {
    			case <- ticker.C: // 时间到了就会触发这个分支的执行,其实时间到了定时器会往ticker.C 这个 channel 中写一条数据,随后被 select 捕捉到channel中有数据可读,就读取channel数据,执行相应分支的语句
    				printDemo()
    			case <- stopFlag: // 定时任务进程在监听定时器的同时也监听这个无缓冲泳道,如果监听到无缓冲泳道的消息,则立刻 return 终止协程,也就终止了定时任务。
    				return
    			}
    		}
    	}()
    }
    
    func main(){
    
      // 初始化定时器,每 5s 会打印一个「demo........」
      InitDemoScheduler()
      
      // 等待,避免主线程退出,实际应用时这里可以时启动 http 服务器的监听动作,或者启动 rpc 服务的监听动作,所以不需要 sleep
      time.sleep(100*time.Second)
    }
    
    

    二、另一种 定时器 Ticker

    这种定时器 除了创建定时器调用的是 time.Tick 而不是 time.NewTicker 外,用法跟 NewTicker 完全相同,区别是 Ticker 无法被关闭停止,所以也不需要在 defer 中关闭 Ticker

    tick := time.Tick(time.Second)

  • 相关阅读:
    2020.10.31
    2020.10.26
    2020.10.29
    2020.10.28动手动脑+验证
    2020.11.1(每周学习总结)
    2020.10.30
    2020.11.2动手动脑➕课后试验性问题
    Javascript权威指南阅读笔记第3章类型、值和变量(1)
    【一个小功能】从js判断ie版本,浅谈navigator对象的appName属性
    【Vue】vif与vshow的区别
  • 原文地址:https://www.cnblogs.com/hi3254014978/p/16692880.html
Copyright © 2020-2023  润新知