Golang创建定时程序

前两天用Golang手写MapReduce框架时,遇到这样一个问题: > worker可能随时crash,那么coordinator分配给worker的任务不能无限等待worker的回复,因此需要给每个分配出去的任务设置timeout,也就是需要一个定时程序清理crash/太慢的worker的任务。

Golang对并发的原生支持是比较好的,直观而言,只需要在某个goroutine里等待timeout,然后重置这个任务即可。这里我们用“更像Golang”的方法来完成这个任务。

Golang time.Timer

time.Timer是Golang标准库中提供的定时器。用如下代码可以创建一个新定时器(定时10秒):

1
timer := time.NewTimer(time.Second * 10)

Timer有一个成员C,类型是chan Time的channel,根据官方文档: > The Timer type represents a single event. When the Timer expires, the current time will be sent on C, unless the Timer was created by AfterFunc. A Timer must be created with NewTimer or AfterFunc.

可见如果用上面的NewTimer方法指定计时,那么Timer到期后,会给C发送时间。

再注意到Golang中channel是会阻塞的,我们可以用这个方法判断是否到时。

定时程序

首先在调用者函数中创建定时器Timer,接着启动一个goroutine来等候Timer的信号,这时调用者函数可以继续做其他工作。

timer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"time"
)

func main() {
done := false

// set up timer
fmt.Println("10s timeout set up")
timer := time.NewTimer(time.Second * 10)

// goroutine waiting for timeout
go func() {
// will block here before expiring
<-timer.C
// mark the flag as done
fmt.Println("timeout!")
done = true
}()

// do some other work...
for !done {
fmt.Println("still working...")
time.Sleep(time.Second)
}
fmt.Println("finally finished!")
}

上面这段程序输出如下,每句still working...输出间隔1秒:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ go run timer.go
10s timeout set up
still working...
still working...
still working...
still working...
still working...
still working...
still working...
still working...
still working...
still working...
timeout!
finally finished!