【Go】使用Go语言打造定时提醒小工具,从基础到优化全方位探索
一、引言
1.目的和背景
本文和大家分享编程语言对于时间和日期的处理方式,以及代码的优化思路。
2.选择GO语言的原因
- 简单易学:GO语言的语法简单易学,这使得初学者能够快速上手,减少学习成本。
- 高效性能:GO语言的编译器可以将代码转换成本地机器码,因此它可以提供出色的性能和响应速度。
- 并发支持:GO语言的并发模型使得开发者可以轻松地编写高并发应用程序,而无需关注底层细节。
- 开源社区:GO语言拥有一个强大的开源社区,因此可以使用各种可用的库和工具来帮助开发人员快速完成任务。
- 跨平台支持: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分钟。
四、代码改进
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.接收参数
通过以下方式实现定时器的暂停、继续和关闭:
- 暂停:使用
timer.Stop()
方法停止定时器。可以在定时器到期前任意时间内调用该方法,以暂停定时器。 - 继续:使用
time.NewTimer()
方法重新创建一个定时器,并传递剩余时间作为参数。例如,如果之前创建的定时器是 20 分钟的,而它已经运行了 10 分钟,那么可以重新创建一个定时器,时间为 10 分钟,来继续计时。 - 关闭:使用
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)
}
执行效果如下图所示。
五、总结
好了,这个定时器我们就先写到这里,毕竟,代码优化是永远做不完的。
从需求出发,通过选择 Go 语言和定时器相关的包和函数,实现了最初版本的定时提醒功能。接着,为了优化代码和提醒方式,引入了系统通知,并且使用命令行接收用户指令,实现了暂停、继续和关闭功能。
在实现过程中,我们采用了基于 Go 语言标准库中的 time 包的定时器实现定时提醒的功能。为了优化用户体验,我们使用命令行接收用户指令。
在优化过程中,我们引入了 WaitGroup 来确保所有 goroutine 的同步,同时将定时器的重置和取消的代码进行了封装。此外,我们还对代码进行了错误处理,保证程序的健壮性。
通过不断迭代和优化,我们最终实现了完整的定时提醒功能,提高了用户体验和代码的可维护性。
"Learn from yesterday, live for today, hope for tomorrow. The important thing is not to stop questioning." - Albert Einstein
"从昨天中学习,活在今天,寄望于明天。重要的是不要停止探索。" - 阿尔伯特·爱因斯坦
- 点赞
- 收藏
- 关注作者
评论(0)