首页 > 其他分享 >【Go】使用Go语言打造定时提醒小工具,从基础到优化全方位探索

【Go】使用Go语言打造定时提醒小工具,从基础到优化全方位探索

时间:2023-02-22 12:01:08浏览次数:34  
标签:定时器 20 函数 全方位 Timer GO time Go 定时

一、引言

1.目的和背景

本文和大家分享编程语言对于时间和日期的处理方式,以及代码的优化思路。

2.选择GO语言的原因

  1. 简单易学:GO语言的语法简单易学,这使得初学者能够快速上手,减少学习成本。
  2. 高效性能:GO语言的编译器可以将代码转换成本地机器码,因此它可以提供出色的性能和响应速度。
  3. 并发支持:GO语言的并发模型使得开发者可以轻松地编写高并发应用程序,而无需关注底层细节。
  4. 开源社区:GO语言拥有一个强大的开源社区,因此可以使用各种可用的库和工具来帮助开发人员快速完成任务。
  5. 跨平台支持:GO语言可以跨多个操作系统和硬件架构运行,因此它可以用于开发多种类型的应用程序。

二、GO语言中的时间和定时器

1.时间相关的包和函数

GO语言中有两个与时间相关的包:time和date。time包提供了许多有用的函数和类型来处理时间,包括时间的解析、格式化和计算等。date包则提供了一些与日期相关的函数和类型,例如日期的解析和格式化等。

下面是一些常用的time包函数和类型:

  • time.Now():返回当前本地时间。
  • time.Date():返回一个指定日期和时间的Time类型。
  • time.Sleep():休眠指定的时间。
  • time.Tick():返回一个定时的通道,每隔一段时间发送一个时间值。
  • time.After():返回一个通道,指定时间后发送一个时间值。

除了time包,GO语言还提供了一个time.Timer类型,用于定时器的管理。Timer类型提供了Reset()和Stop()方法,用于重新设置定时器和停止定时器。

2.定时器相关的包和函数

GO语言中的time包提供了一个Ticker类型和一个Timer类型,用于定时器的管理。Ticker类型可以按照一定的时间间隔循环触发事件,而Timer类型可以在指定时间后触发事件。

下面是一些常用的Ticker和Timer相关的函数和类型:

  • time.NewTicker():创建一个Ticker类型的定时器。
  • time.Ticker.C:返回一个定时的通道,每隔一段时间发送一个时间值。
  • time.Ticker.Stop():停止定时器。
  • time.NewTimer():创建一个Timer类型的定时器。
  • time.Timer.C:返回一个通道,定时器到期后发送一个时间值。
  • time.Timer.Reset():重新设置定时器到期时间。
  • time.Timer.Stop():停止定时器。

我们可以使用这些函数和类型来实现一个简单的定时器,例如在20分钟后触发提醒事件。

三、使用GO语言实现功能

package main

import (
	"fmt"
	"time"
)

func main() {
	timer := time.NewTimer(20 * time.Minute) // 创建一个20分钟的定时器
	<-timer.C // 等待定时器到期
	fmt.Println("时间到!请注意您的时间管理。")
}

在上面的代码中,我们使用time.NewTimer()函数创建了一个20分钟的定时器。然后,我们通过从定时器的通道timer.C中读取数据,来等待定时器到期。一旦定时器到期,我们就会收到一个数据,然后在控制台中打印一条提醒信息。

我们在本地编译之后直接执行,为了方便测试,我把时间改成了1分钟。

image-20230222104933297

四、代码改进

1.time.AfterFunc()

当使用 time.NewTimer() 函数创建一个定时器后,程序会阻塞在 <-timer.C 语句处等待定时器到期。如果我们想在等待定时器到期的同时,可以执行其他操作或等待多个定时器同时到期,就需要使用 time.AfterFunc() 函数。

time.AfterFunc() 函数可以设置一个定时器并在定时器到期时,执行指定的回调函数。下面是使用 time.AfterFunc() 函数实现定时器的示例代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	duration := 20 * time.Minute // 定时器时长为20分钟
	timer := time.AfterFunc(duration, func() {
		fmt.Println("时间到了!20分钟已经过去了。")
	})
	defer timer.Stop() // 停止定时器,防止回调函数未执行时程序提前退出
	fmt.Printf("定时器已经设置,等待 %v 后提醒...\n", duration)
	time.Sleep(30 * time.Minute) // 等待30分钟,以便看到定时器的效果
}

我们使用 time.AfterFunc() 函数设置了一个 20 分钟的定时器,并在定时器到期时执行指定的回调函数。与使用 time.NewTimer() 不同,time.AfterFunc() 函数并不会阻塞程序的执行。因此,我们需要使用 time.Sleep() 函数等待一段时间,以便观察定时器的效果。

2.sync.WaitGroup

使用 time.Sleep() 等待定时器到期的方式不够优雅,因为它将阻塞程序的执行。我们可以使用 sync.WaitGroup 来等待定时器到期,并避免阻塞程序的执行。

sync.WaitGroup 是 Go 语言提供的一种同步原语,它允许我们等待一组协程完成它们的任务。在我们的例子中,我们可以使用 WaitGroup 来等待定时器到期。具体来说,我们可以创建一个 WaitGroup,然后在启动协程之前调用 Add(1) 方法来增加计数器的值,表示有一个协程需要等待。然后在协程中等待定时器到期,并在定时器到期后调用 Done() 方法,将计数器减一。最后,我们可以使用 Wait() 方法来等待计数器的值减少到零,表示所有协程都已经完成它们的任务。

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	fmt.Println("定时器已经设置,等待 20m0s 后提醒...")
	var wg sync.WaitGroup
	wg.Add(1)
	time.AfterFunc(20*time.Minute, func() {
		fmt.Println("时间到了!20分钟已经过去了。")
		wg.Done()
	})
	wg.Wait()
}

3.接收参数

通过以下方式实现定时器的暂停、继续和关闭:

  1. 暂停:使用 timer.Stop() 方法停止定时器。可以在定时器到期前任意时间内调用该方法,以暂停定时器。
  2. 继续:使用 time.NewTimer() 方法重新创建一个定时器,并传递剩余时间作为参数。例如,如果之前创建的定时器是 20 分钟的,而它已经运行了 10 分钟,那么可以重新创建一个定时器,时间为 10 分钟,来继续计时。
  3. 关闭:使用 os.Exit() 方法退出程序。
package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

var timer *time.Timer
var isPaused bool

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 输入提醒时间
	fmt.Println("请输入提醒时间(单位:分钟):")
	durationStr, _ := reader.ReadString('\n')
	duration, _ := strconv.Atoi(strings.TrimSpace(durationStr))
	durationDuration := time.Duration(duration) * time.Minute

	// 创建定时器
	timer = time.NewTimer(durationDuration)
	fmt.Printf("定时器已经设置为 %d 分钟\n", duration)

	// 开始提醒
	go func() {
		for {
			if isPaused {
				continue
			}

			<-timer.C
			sendNotification("时间到了,休息一下吧")
		}
	}()

	// 监听命令行输入
	for {
		commandStr, _ := reader.ReadString('\n')
		command := strings.TrimSpace(commandStr)

		switch command {
		case "pause":
			isPaused = true
			timer.Stop()
			fmt.Println("提醒已暂停")
		case "resume":
			isPaused = false
			timer.Reset(durationDuration)
			fmt.Println("提醒已恢复")
		case "quit":
			fmt.Println("程序已退出")
			return
		default:
			fmt.Println("请输入正确的指令")
		}
	}
}

// 发送通知
func sendNotification(message string) {
	cmd := exec.Command("osascript", "-e", fmt.Sprintf(`display notification "%s" with title "休息提醒"`, message))
	_ = cmd.Run()
	fmt.Printf("【提醒】%s\n", message)
}

执行效果如下图所示。

image-20230222113432645

五、总结

好了,这个定时器我们就先写到这里,毕竟,代码优化是永远做不完的。

从需求出发,通过选择 Go 语言和定时器相关的包和函数,实现了最初版本的定时提醒功能。接着,为了优化代码和提醒方式,引入了系统通知,并且使用命令行接收用户指令,实现了暂停、继续和关闭功能。

在实现过程中,我们采用了基于 Go 语言标准库中的 time 包的定时器实现定时提醒的功能。为了优化用户体验,我们使用命令行接收用户指令。

在优化过程中,我们引入了 WaitGroup 来确保所有 goroutine 的同步,同时将定时器的重置和取消的代码进行了封装。此外,我们还对代码进行了错误处理,保证程序的健壮性。

通过不断迭代和优化,我们最终实现了完整的定时提醒功能,提高了用户体验和代码的可维护性。

"Learn from yesterday, live for today, hope for tomorrow. The important thing is not to stop questioning." - Albert Einstein

"从昨天中学习,活在今天,寄望于明天。重要的是不要停止探索。" - 阿尔伯特·爱因斯坦

img

标签:定时器,20,函数,全方位,Timer,GO,time,Go,定时
From: https://blog.51cto.com/u_13738137/6078854

相关文章

  • GO 管理微信公众号后台(wechat)
    2023-02-2209:57:10星期三目的微信公众号后台管理,实现简单的文本接收与信息回复。前提配置好微信服务器,并正常启动安装了golang安装了wechat包示例代码演示......
  • Django - 创建工程 和 APP 与 开发工具
    1.安装在这里使用的是Anaconda,直接使用Anaconda命令进行安装;(E:\Anaconda3)C:\Users\yuan>condainstalldjango//安装指定版本django(E:\Anaconda3)C:\Users\yuan>con......
  • go 关闭chan 广播信号
    packagemainimport( "fmt" "sync")funchello(num...int){ num[0]=18}funcmain(){ closeChan:=make(chanstruct{}) wg:=&sync.WaitGroup{} fo......
  • python 删除大于超过一定时间文件
    importosfromdatetimeimportdatetime,timedeltapath="."if__name__=='__main__':forroot,firs,filesinos.walk(path):forfileinfiles......
  • openapi generator(go学习)
    一.安装通过npm安装,其他安装方式请见:https://openapi-generator.tech/docs/installation/#安装脚手架npminstall@openapitools/openapi-generator-cli-g#设置使......
  • django解决网站CORS前后端跨域问题
    1.安装cors-headers⼯具  pipinstalldjango-cors-headers 2.安装cors-headers应⽤#注册应用INSTALLED_APPS=['django.contrib.admin','django.con......
  • golang入门(十二)并发channel
    多线程与多进程本质的区别在于,多线程的内存空间是共享的,多进程是每一个进程都会独立开辟一块内存空间。如果我们运行的多个任务是完全独立的,那么在资源足够的情况下并发还是......
  • 03-Go数组、切片、可变长参数、maps
    1数组#1数组是类似于数字、字符串、的基础数据类型#2数组是同一类型元素的集合eg:整数集合:5,8,9,79,76形成一个数组Go--数组:不允许混合不同类型......
  • Go 中的反射 reflect 介绍和基本使用
    一、什么是反射在计算机科学中,反射(英语:reflection)是指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候......
  • 基于Vue项目+django写一个登录的页面
    基于Vue项目+django写一个登录的页面前端借用了一下vue项目模板的AboutView.vue页面组件<template><divclass="about"><h1>登录功能</h1><p>输入用户名......