有没有办法在 Go 中执行重复的后台任务?我正在考虑 Java 中类似
Timer.schedule(task, delay, period)
的东西。我知道我可以使用 goroutine 和 Time.sleep()
来做到这一点,但我想要一些可以轻松停止的东西。
这是我得到的,但对我来说看起来很难看。有更干净/更好的方法吗?
func oneWay() {
var f func()
var t *time.Timer
f = func () {
fmt.Println("doing stuff")
t = time.AfterFunc(time.Duration(5) * time.Second, f)
}
t = time.AfterFunc(time.Duration(5) * time.Second, f)
defer t.Stop()
//simulate doing stuff
time.Sleep(time.Minute)
}
time.NewTicker
创建一个发送周期性消息的通道,并提供一种停止它的方法。像这样使用它(未经测试):
ticker := time.NewTicker(5 * time.Second)
quit := make(chan struct{})
go func() {
for {
select {
case <- ticker.C:
// do stuff
case <- quit:
ticker.Stop()
return
}
}
}()
您可以通过关闭
quit
通道来停止工作人员:close(quit)
。
如果您不关心刻度移动(取决于之前每次执行花费的时间)并且您不想使用通道,则可以使用本机范围函数。
即
package main
import "fmt"
import "time"
func main() {
go heartBeat()
time.Sleep(time.Second * 5)
}
func heartBeat() {
for range time.Tick(time.Second * 1) {
fmt.Println("Foo")
}
}
像这样的东西怎么样
package main
import (
"fmt"
"time"
)
func schedule(what func(), delay time.Duration) chan bool {
stop := make(chan bool)
go func() {
for {
what()
select {
case <-time.After(delay):
case <-stop:
return
}
}
}()
return stop
}
func main() {
ping := func() { fmt.Println("#") }
stop := schedule(ping, 5*time.Millisecond)
time.Sleep(25 * time.Millisecond)
stop <- true
time.Sleep(25 * time.Millisecond)
fmt.Println("Done")
}
查看这个库:https://github.com/robfig/cron
示例如下:
c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()
我使用以下代码:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println("\nToday:", now)
after := now.Add(1 * time.Minute - time.Second)
fmt.Println("\nAdd 1 Minute:", after)
for {
fmt.Println("test")
time.Sleep(10 * time.Second)
now = time.Now()
if now.After(after) {
break
}
}
fmt.Println("done")
}
它更简单,对我来说效果很好。
这个问题的更广泛的答案可能会考虑 Occam 中经常使用的乐高积木方法,并通过 JCSP 提供给 Java 社区。 Peter Welch 有一个非常好的 演讲 阐述了这个想法。
这种即插即用方法可以直接转换为 Go,因为 Go 使用与 Occam 相同的通信顺序过程基础原理。
因此,在设计重复性任务时,您可以将系统构建为由简单组件(如 goroutine)组成的数据流网络,这些组件通过通道交换事件(即消息或信号)。
这种方法是组合的:每组小组件本身都可以充当一个更大的组件,无穷无尽。这可能非常强大,因为复杂的并发系统是由易于理解的砖块组成的。
脚注:在 Welch 的演讲中,他对通道使用了奥卡姆语法,即 ! 和 ?,它们直接对应于 Go 中的 ch<- 和 <-ch。
您可以像这样简单地使用 gocron 包:
package main
import (
"fmt"
"time"
// go get github.com/go-co-op/gocron
"github.com/go-co-op/gocron"
)
func main() {
s := gocron.NewScheduler(time.UTC)
s.Every(3).Seconds().Do(func() { fmt.Println("Every 3 seconds") })
// you can start running the scheduler in two different ways:
// starts the scheduler asynchronously
s.StartAsync()
// starts the scheduler and blocks current execution path
// s.StartBlocking()
}
我认为这更简单:
for ; ; {
time.Sleep(time.Until(until))
doTheDamnThing()
}