学习了一个go的并发模式:规定时间内跑完几个函数,这几个函数在另一个goroutine中跑。如果跑的过程中遇到中断或者超过规定的时间还是没有跑完,都返回错误的状态。
结果如下(规定时间内没有跑完就timeout,退出):
root@ubuntu:my_go# go run runner.go
2018/05/07 08:44:14 Starting work.
2018/05/07 08:44:14 Processor - Task #0.
2018/05/07 08:44:14 Processor - Task #1.
2018/05/07 08:44:15 Processor - Task #2.
2018/05/07 08:44:17 Terminating due to timeout.
exit status 1
root@ubuntu:my_go#
代码如下:
package main
import (
"errors"
"os"
"os/signal"
"time"
"log"
)
//Runner 在给定的超时时间内执行一组任务,
//并且在操作系统发送中断信号时结束这些任务
type Runner struct {
interrupt chan os.Signal
complete chan error
//只读chan, chan<- 是只写chan
timeout <-chan time.Time
tasks []func(int)
}
var ErrTimeout = errors.New("received timeout")
var ErrInterrupt = errors.New("received interrupt")
func Runner_New(d time.Duration) *Runner {
return &Runner {
//这里使用缓冲通道,因为后面读取interrupt时使用的是非阻塞select
//这样即使系统没有给interrupt通道发送消息,也不会阻塞我们跑tasks里面的东西
//即给缓冲通道添加消息时,如果通道是空的,不会别阻塞,如果是非缓冲chan,会阻塞的。
interrupt: make(chan os.Signal, 1),
//这里使用阻塞通道,是因为Start用另一个go去跑tasks之后,用了阻塞的select,
//并且tasks跑完之后,也需要Start真的收到了complete才能结束goroutine
complete: make(chan error),
//After(d)会返回一个通道,并起另一个goroutine,在d到时时向这个通道发送一个time.Time的值
timeout: time.After(d),
}
}
//将一个函数添加到tasks上, ...是可变参数指示
func (r *Runner) Add(tasks ...func(int)) {
r.tasks = append(r.tasks, tasks...)
}
//执行所有任务,并监视通道事件
func (r *Runner) Start() error {
//所有的interrupt、中断信号都要通知我们
signal.Notify(r.interrupt, os.Interrupt)
go func() {//注意这个匿名函数是在另一个goroutine中执行的,
//执行后通过complete通道返回给r.complete
//r.run()的返回值写到 r.complete channel,
r.complete <- r.run()
}()
//另一个go协程正在跑,这里没有default,所以是阻塞等待
select {
case err := <-r.complete:
return err
//如果是这里返回,则说明超时了,函数还没有跑完
case <-r.timeout:
return ErrTimeout
}
}
func (r *Runner) run() error {
//把tasks中所有的函数都拿出来跑
for id, task := range r.tasks {
if r.gotInterrupt() {
return ErrInterrupt
}
//task是回调任务,参数是id
task(id)
}
//正常结束,就返回nil的errr
return nil
}
func (r *Runner) gotInterrupt() bool {
//这里有default,所以是非阻塞。
select {
case <-r.interrupt:
//将r.interrupt停止,不用再通知我了
signal.Stop(r.interrupt)
return true
//系统没有通知我们interrupt,所以return false
default:
return false
}
}
const timeout = 3 * time.Second
func main() {
log.Println("Starting work.")
//创建一个Runner
r := Runner_New(timeout)
r.Add(createTask(), createTask(), createTask())
//这里的r.Start()会阻塞,直到所有task 跑完,或者超时,或者中断返回
if err := r.Start(); err != nil {//err != nil,说明执行过程中有错误
switch err {
case ErrTimeout:
log.Println("Terminating due to timeout.")
os.Exit(1)
case ErrInterrupt:
log.Println("Terminating due to interrupt.")
os.Exit(2)
}
}
log.Println("Process ended.")
}
//返回值是 func(int) 类型的函数
func createTask() func(int) {
return func(id int) {
log.Printf("Processor - Task #%d.", id)
time.Sleep(time.Duration(id) * time.Second)
}
}