【Golang】关于Go语言中的定时器原理与实战应用

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Go语言开发零基础到高阶实战
景天的主页:景天科技苑

在这里插入图片描述

文章目录

  • Go语言中的定时器
    • 一、Timer定时器
      • 1. 创建Timer
      • 2. 停止Timer
      • 3. 重置Timer
      • 4. time.AfterFunc
      • 5. time.After
    • 二、Ticker定时器
      • 1. 创建Ticker
      • 2. 监听Ticker事件
      • 3. 停止Ticker定时器
    • 三、定时器应用案例
      • 1. 定时打印日志
      • 2. 周期性检查系统状态
    • 四、总结

Go语言中的定时器

在Go语言中,定时器是并发编程中常用的工具之一。定时器可以用于监控某个goroutine的运行时间、定时打印日志、周期性执行任务等多种场景。
Go标准库提供了两种主要的定时器:Timer(一次性定时器)和Ticker(周期性定时器)。本文将详细介绍这两种定时器的用法,并通过实际案例展示其应用场景。

一、Timer定时器

Timer定时器是一种一次性定时器,即在未来某个时刻触发的事件只会执行一次。
Timer的结构中包含一个Time类型的管道C,主要用于事件通知。
在未到达设定时间时,管道内没有数据写入,一直处于阻塞状态;到达设定时间后,会向管道内写入一个系统时间,触发事件。

1. 创建Timer

使用time.NewTimer函数可以创建一个Timer定时器。该函数接受一个Duration类型的参数,表示定时器的超时时间,并返回一个*Timer类型的指针。
看下源码,Timer结构体中,timer.C是一个时间类型的通道
在这里插入图片描述

package main

import (
    "fmt"
    "time"
)

func main() {
    // func NewTimer(d Duration) *Timer
    timer := time.NewTimer(2 * time.Second) // 设置超时时间2秒
    // 先打印下当前时间
    fmt.Println("当前时间:", time.Now())

    //我们可以打印下这个只读通道的值
        //timer.C就是我们在定义定时器的时候,存放的时间,等待对应的时间。现在这个就是根据当前时间加2秒

    fmt.Println("通道里面的值", <-timer.C)
}

可以看到timer.C这个只读通道里面的值,就是通过NewTimer设置时间间隔后的时间
在这里插入图片描述

因此,我们可以根据时间通道,来定时将来某个时间要做的事

package main

import (
    "fmt"
    "time"
)

func main() {
    timer := time.NewTimer(2 * time.Second) // 设置超时时间2秒
    <-timer.C
    //经过两秒后只想下面代码
    fmt.Println("after 2s Time out!")
}

在这里插入图片描述

在上述代码中,创建了一个超时时间为2秒的定时器,程序会阻塞在<-timer.C处,直到2秒后定时器触发,程序继续执行并打印“after 2s Time out!”。

2. 停止Timer

使用Stop方法可以停止一个Timer定时器。该方法返回一个布尔值,表示定时器是否在超时前被停止。
如果返回true,表示定时器在超时前被成功停止;如果返回false,表示定时器已经超时或已经被停止过。
在这里插入图片描述

package main

import (
    "fmt"
    "time"
)

func main() {
    timer := time.NewTimer(2 * time.Second) // 设置超时时间2秒
    //这里,创建定时器后,里面执行了停止方法,肯定是在定时器超时前停止了,返回true
    res := timer.Stop()
    fmt.Println(res) // 输出:true
}

在上述代码中,创建了一个超时时间为2秒的定时器,并立即调用Stop方法停止它。由于定时器还没有超时,所以Stop方法返回true。

3. 重置Timer

对于已经过期或者是已经停止的Timer,可以通过Reset方法重新激活它,并设置新的超时时间。Reset方法也返回一个布尔值,表示定时器是否在重置前已经停止或过期。
在这里插入图片描述

package main

import (
    "fmt"
    "time"
)

func main() {
    timer := time.NewTimer(2 * time.Second)
    <-timer.C
    fmt.Println("time out1")
    //经过两秒后,定时器超时了
    res1 := timer.Stop()
    //此时再stop,得到的是false
    fmt.Printf("res1 is %t\n", res1) // 输出:false
    //然后我们重置定时器。重置成3秒后超时
    timer.Reset(3 * time.Second)
    res2 := timer.Stop()
    //此时再stop,由于定时器没超时,得到的是true
    fmt.Printf("res2 is %t\n", res2) // 输出:true
}

在这里插入图片描述

在上述代码中,首先创建了一个超时时间为2秒的定时器,并在超时后打印“time out1”。
然后调用Stop方法停止定时器,由于定时器已经过期,所以Stop方法返回false。
接着调用Reset方法将定时器重新激活,并设置新的超时时间为3秒。
最后再次调用Stop方法停止定时器,由于此时定时器还没有过期,所以Stop方法返回true。

4. time.AfterFunc

time.AfterFunc函数可以接受一个Duration类型的参数和一个函数f,返回一个*Timer类型的指针。在创建Timer之后,等待一段时间d,然后执行函数f。

package main

import (
    "fmt"
    "time"
)

func main() {
    duration := time.Duration(1) * time.Second
    f := func() {
        fmt.Println("f has been called after 1s by time.AfterFunc")
    }
    // func AfterFunc(d Duration, f func()) *Timer
    //等待duration时间后,执行f函数
    //这里是经过1秒后。执行f函数
    timer := time.AfterFunc(duration, f)
    defer timer.Stop()
    time.Sleep(2 * time.Second)
}

在上述代码中,创建了一个超时时间为1秒的定时器,并在超时后执行函数f。
使用defer语句确保在程序结束时停止定时器。程序会在1秒后打印“f has been called after 1s by time.AfterFunc”。

5. time.After

time.After函数会返回一个*Timer类型的管道,该管道会在经过指定时间段d后写入数据。调用这个函数相当于实现了一个定时器。
在这里插入图片描述

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)
    go func() {
        time.Sleep(3 * time.Second)
        ch <- "test"
    }()
    
    //使用select,哪个先到来,耗时时间短,执行哪个
    select {
    case val := <-ch:
        fmt.Printf("val is %s\n", val)
    // 这个case是两秒后执行
    // func After(d Duration) <-chan Time
    case <-time.After(2 * time.Second):
        fmt.Println("timeout!!!")
    }
}

在这里插入图片描述

在上述代码中,创建了一个管道ch,并在另一个goroutine中等待3秒后向管道写入数据。
在主goroutine中使用select语句监听两个管道:一个是刚刚创建的ch,另一个是time.After函数返回的管道c。
由于ch管道3秒后才会有数据写入,而time.After函数是2秒超时,所以2秒后select会先收到管道c里的数据,执行“timeout!!!”并退出。

二、Ticker定时器

Ticker定时器可以周期性地不断触发时间事件,不需要额外的Reset操作。Ticker的结构中也包含一个Time类型的管道C,每隔固定时间段d就会向该管道发送当前的时间,根据这个管道消息来触发事件。

Ticker定时器是Go标准库time包中的一个重要组件。它允许你每隔一定的时间间隔执行一次指定的操作。Ticker定时器在创建时会启动一个后台goroutine,该goroutine会按照指定的时间间隔不断向一个通道(Channel)发送当前的时间值。

1. 创建Ticker

要创建一个Ticker定时器,你可以使用time.NewTicker函数。这个函数接受一个time.Duration类型的参数,表示时间间隔,并返回一个*time.Ticker类型的指针。
Ticker定时器的核心是一个通道(Channel),你可以通过监听这个通道来接收时间间隔到达的事件。

ticker := time.NewTicker(1 * time.Second)

上面的代码创建了一个每隔1秒触发一次的Ticker定时器。

2. 监听Ticker事件

要监听Ticker定时器的事件,你可以使用range关键字或者select语句来监听Ticker定时器的通道。每次时间间隔到达时,Ticker定时器的通道都会接收到一个当前的时间值。

for range ticker.C {  
    // 在这里执行周期性任务  
}
package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(1 * time.Second)
    //查看定时器数据类型
    fmt.Printf("定时器数据类型%T\n", ticker)
    //启动协程来监听定时器触发事件,通过time.Sleep函数来等待5秒钟,然后调用ticker.Stop()函数来停止定时器。
    //最后,输出"定时器停止"表示定时器已经成功停止。
    go func() {
        for range ticker.C {
            fmt.Println("Ticker ticked")
        }

    }()

    //执行5秒后,让定时器停止
    time.Sleep(5 * time.Second)
    ticker.Stop()
    fmt.Println("定时器停止")
}

在这里插入图片描述

或者,如果你需要同时监听多个通道,你可以使用select语句:

select {  
case t := <-ticker.C:  
    // 处理Ticker定时器事件  
case <-stopChan:  
    // 处理停止信号  
}
package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(1 * time.Second) // 创建一个每秒触发一次的Ticker定时器
    defer ticker.Stop()                       // 确保在main函数结束时停止定时器

    for {
        select {
        case t := <-ticker.C:
            fmt.Println("Tick at", t)
        }
    }
}

每秒执行一次
在这里插入图片描述

3. 停止Ticker定时器

当你不再需要Ticker定时器时,你应该调用它的Stop方法来停止它。停止Ticker定时器可以释放与之关联的资源,并防止不必要的goroutine继续运行。
ticker.Stop()

停止Ticker定时器后,它的通道将不再接收任何事件。

package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(500 * time.Millisecond) // 创建一个每500毫秒触发一次的Ticker定时器
    timeEnd := make(chan bool)                       // 用于停止Ticker定时器的通道

    go func() {
        for {
            select {
            //当达到设置的停止条件式,停止循环
            case <-timeEnd:
                fmt.Println("===结束任务")
                break
            case t := <-ticker.C:
                fmt.Println("==500毫秒响应一次:", t)
            }
        }
    }()

    time.Sleep(5 * time.Second) // 主线程等待5秒钟
    ticker.Stop()               // 停止Ticker定时器
    timeEnd <- true             // 发送结束信号

    fmt.Println("===定时任务结束===")
}

持续执行5秒后,定时器停止运行
在这里插入图片描述

三、定时器应用案例

1. 定时打印日志

Ticker定时器的一个常见应用是定时打印日志。通过设置一个Ticker定时器,你可以每隔固定的时间间隔输出一次日志信息,从而监控程序的运行状态。

package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(5 * time.Second)
    defer ticker.Stop() // 确保程序结束时停止Ticker定时器

    for range ticker.C {
        // 打印当前时间作为日志
        fmt.Println("Current time:", time.Now())
    }
}

5秒打印一次
在这里插入图片描述

在这个例子中,我们创建了一个每隔5秒触发一次的Ticker定时器,并在一个无限循环中监听它的事件。
每次事件触发时,我们都会打印当前的时间作为日志信息。注意,由于我们在main函数中使用了defer语句来确保Ticker定时器在程序结束时被停止,所以即使循环是无限的,程序也不会因为Ticker定时器而泄漏资源。

然而,在实际应用中,你可能需要在某个条件下提前停止Ticker定时器。这时,你可以使用一个额外的通道来发送停止信号:

package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(5 * time.Second)
    stopChan := make(chan struct{})

    go func() {
        // 模拟一个运行一段时间的任务
        time.Sleep(15 * time.Second)
        // 发送停止信号
        stopChan <- struct{}{}
    }()

    for {
        select {
        case t := <-ticker.C:
            fmt.Println("Tick at", t)
        case <-stopChan:
            fmt.Println("Ticker stopped")
            ticker.Stop()
            return
        }
    }
}

在这里插入图片描述

在这个例子中,我们创建了一个额外的stopChan通道来发送停止信号。我们启动了一个goroutine来模拟一个运行一段时间的任务,并在任务完成后向stopChan发送一个停止信号。
在for循环中,我们使用select语句同时监听Ticker定时器的通道和stopChan通道。当接收到停止信号时,我们停止Ticker定时器并退出程序。

2. 周期性检查系统状态

Ticker定时器还可以用于周期性检查系统状态。例如,你可以每隔一段时间检查一次服务器的负载、内存使用情况或数据库连接数等关键指标,并在发现异常时采取相应的措施。

package main

import (
    "fmt"
    "math/rand"
    "time"
)

// 模拟检查系统状态的函数
func checkSystemStatus() {
    // 这里可以添加实际的检查逻辑
    // 例如:检查CPU使用率、内存使用情况等
    // 这里我们随机生成一个0到100之间的数作为模拟结果
    status := rand.Intn(101)
    fmt.Printf("System status: %d\n", status)

    // 假设状态大于80表示系统异常
    if status > 80 {
        fmt.Println("Warning: System status is above normal!")
        // 这里可以添加处理异常的逻辑
        // 例如:发送警报、重启服务等
    }
}

func main() {
    ticker := time.NewTicker(10 * time.Second)
    defer ticker.Stop() // 确保程序结束时停止Ticker定时器

    for range ticker.C {
        checkSystemStatus()
    }
}

在这里插入图片描述

在这个例子中,我们创建了一个每隔10秒触发一次的Ticker定时器,并在一个无限循环中监听它的事件。
每次事件触发时,我们都会调用checkSystemStatus函数来模拟检查系统状态。checkSystemStatus函数会随机生成一个0到100之间的数作为模拟结果,并根据结果判断是否系统异常。
如果系统异常(即状态大于80),则打印警告信息,并可以在这里添加处理异常的逻辑。

同样地,你可以使用额外的通道来发送停止信号,以便在需要时提前停止Ticker定时器。

四、总结

本文详细介绍了Go语言中Timer和Ticker两种定时器的用法,并通过实际案例展示了它们的应用场景。Timer定时器适用于需要一次性触发的事件,而Ticker定时器适用于需要周期性触发的事件,希望在大家go的学习与应用上能够帮助到大家。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/892343.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QRTCN区间预测 | Matlab实现QRTCN时间卷积神经网络分位数回归区间预测

区间预测 | Matlab实现QRTCN时间卷积神经网络分位数回归区间预测 目录 区间预测 | Matlab实现QRTCN时间卷积神经网络分位数回归区间预测预测效果基本介绍模型特性程序设计参考资料预测效果 基本介绍 Matlab实现QRTCN时间卷积神经网络分位数回归区间预测 QRTCN(Quantile Regres…

2.mybatis-plus3.x的使用

官网&#xff1a;简介 | MyBatis-Plushttps://baomidou.com/introduce/ 3.X版本插件使用、 1. 分页插件 配置插件&#xff08;不能用的情况去官网看看最新的&#xff09; Configuration MapperScan("scan.your.mapper.package") public class MybatisPlusConfig …

Django 定义使用模型,并添加数据

教材&#xff1a; Python web企业级项目开发教程&#xff08;黑马程序员&#xff09;第三章 模型 实验步骤&#xff1a; 1.创建项目和应用 前置步骤可看前文&#xff0c;进入到指定文件位置后创建 django-admin startproject mysite python manage.py startapp app01 2.注册…

DBA | 如何将 .bak 的数据库备份文件导入到SQL Server 数据库中?

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] 原文链接&#xff1a;DBA | 如何将 .bak 的数据库备份文件导入到SQL Server 数据库中? 如何将&#xff08;.bak&#xff09;的SQL Server 数据库备份文件导入到当前数据库中? Step 1.登录到 Sql…

【专题】智启未来:新质生产力引擎驱动下的智能制造行业革新报告合集PDF分享(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p37856 在当今全球经济格局深刻变革的大背景下&#xff0c;制造业作为国家经济的基石&#xff0c;正处在高质量发展的关键历史时期。智能决策作为一股崭新的力量&#xff0c;正逐步成为推动制造业数智化转型的强大新动能。众多制造企…

每日OJ题_牛客_对称之美_哈希_C++_Java

目录 牛客_对称之美_哈希 题目解析 C代码 Java代码 牛客_对称之美_哈希 对称之美 (nowcoder.com) 描述&#xff1a; 给出n个字符串&#xff0c;从第1个字符串一直到第n个字符串每个串取一个字母来构成一个新字符串&#xff0c;新字符串的第i个字母只能从第i行的字…

安卓13屏蔽蓝牙匹配对话框 自动匹配 android13屏蔽蓝牙匹配对话框 自动匹配

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 设置 蓝牙连接的时候,会有匹配对话框提示。我们来实现自动配对。 2.问题分析 这里我们是通过点击操作来实现功能的,所以我们思路可以是自动点击功能的实现。 3.代码分…

Ubuntu 24.04 在 BPI-F3 上通过 SD 卡安装并从 NVME 运行

github 代码&#xff1a; https://github.com/rcman/BPI-F3 Ubuntu 24.04 现在正在我的 BPI-F3 上运行。很快会为 YouTube 制作一个视频。 这应该适用于任何版本的 Linux&#xff0c;仅在 Ubuntu 24.04 上测试过 入门 下载 Bianbu映像并使用您最喜欢的工具将其映像到微型 SD 卡…

选择排序-求和表达式

概述 选择排序算法复杂度计算&#xff0c;用到了求和表达式。&#xff08;以前学过&#xff0c;现在都忘了&#xff0c;记录一下&#xff09; 理解求和表达式的关键在于逐步拆分和简化。让我们一步一步来详细解释这个求和表达式&#xff1a; 求和表达式 求和表达式 分步求解 …

ESP32C3 开发板在Linux环境下,进行JTAG 调试演示-启明云端乐鑫代理商

JTAG 调试教程 本教程主要演示 esp32c3 开发板在 linux 环境下&#xff0c;通过 vscode 的 esp-idf 插件使用 jtag 调试工具。 esp32c3 不但内置了USB-JTAG&#xff0c;还内置了USB-SERIAL&#xff0c;仅需要一根USB线即可实现下载和调试仿真。 下面演示调试仿真的过程。 创…

系统思考—抓重点

最近与一些中小微企业的老板交流时&#xff0c;深刻感受到大家都陷入了一种忙碌的困境。在资源有限的情况下&#xff0c;到底该抓住什么&#xff1f;这个问题不仅关乎未来的战略&#xff0c;也与预算息息相关。面对复杂多变的市场环境&#xff0c;许多企业尝试了各种方法&#…

rpa批量发送邮件如何通过编辑器编发邮件?

rpa批量发送邮件的技巧&#xff1f;怎么使用rpa邮箱群发助手&#xff1f; 手动发送邮件变得越来越繁琐且效率低下。为了解决这一问题&#xff0c;越来越多的企业开始采用RPA技术来批量发送邮件。AokSend将详细探讨如何通过编辑器来实现rpa批量发送邮件的功能&#xff0c;从而提…

【JVM】—深入理解G1回收器——概念详解

深入理解G1回收器——概念详解 ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记链接&#x1f449;https://github.com/A-BigTree/Code_Learning ⭐⭐⭐⭐⭐⭐ 如果可以&#xff0c;麻烦各位看官顺手点个star~&#x1f60a; 文章目录 深入理解G1回收器…

OceanBase 4.x 部署实践:如何从单机扩展至分布式部署

OceanBase 4.x 版本支持2种部署模式&#xff1a;单机部署与分布式部署&#xff0c;同时支持从单机平滑扩展至分布式架构。这样&#xff0c;可以有效解决小型业务向大型业务转型时面临的扩展难题&#xff0c;降低了机器资源的成本。 以下将详述如何通过命令行&#xff0c;实现集…

【JavaScript】LeetCode:71-75

文章目录 71 搜索插入位置72 搜索二维矩阵73 在排序数组中查找元素的第一个和最后一个位置74 搜索旋转排序数组75 寻找旋转排序数组中的最小值 71 搜索插入位置 二分查找在最后一轮比较中&#xff0c;mid所指向的值 > target&#xff0c;right往左收&#xff0c;此时left所指…

容器实战高手课---09 Page Cache:为什么我的容器内存使用量总是在临界点

你好&#xff0c;我是程远。 上一讲&#xff0c;我们讲了Memory Cgroup是如何控制一个容器的内存的。我们已经知道了&#xff0c;如果容器使用的物理内存超过了Memory Cgroup里的memory.limit_in_bytes值&#xff0c;那么容器中的进程会被OOM Killer杀死。 不过在一些容器的使…

MybatisPlus分页Page插件

分页Page插件 首先&#xff0c;要在配置类中注册MyBatisPlus的核心插件&#xff0c;同时添加分页插件。设置分页查询的配置类,interceptor只有拦截作用&#xff0c;功能需要自己添加。这里我们添加上分页查询功能。 import com.baomidou.mybatisplus.annotation.DbType; impo…

Java中的数组

一、数组的创建及初始化 1、创建数组 int 表示数组中元素类型 int[] 表示数组的类型 array 表示数组名 2、数组初始化 数组初始化可分为动态初始化和静态初始化&#xff0c;动态初始化只初始化数组的大小&#xff0c;而静态初始化是直接给出数组中的具体元素 动态初始化&am…

dlib库实现人脸检测

摘要 本文将向您介绍如何使用dlib库在图片以及视频中实现人脸识别检测。通过简单的Python代码&#xff0c;我们将展示如何定位图片中的人脸并绘制边框。 引言 人脸识别技术在当今世界越来越普及&#xff0c;应用场景广泛&#xff0c;如安全监控、身份认证、图像处理等。dlib…

OpenCV高级图形用户界面(11)检查是否有键盘事件发生而不阻塞当前线程函数pollKey()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 轮询已按下的键。 函数 pollKey 无等待地轮询键盘事件。它返回已按下的键的代码或如果没有键自上次调用以来被按下则返回 -1。若要等待按键被按…