go 通道实现超时控制、接收系统退出

package main

import (
    "os"
    "time"
    "errors"
    "os/signal"
    "log"
)

type Runner struct {
    interrupt chan os.Signal   //操作系统信号
    complete  chan error       // 运行完成时返回信息
    timeout   <-chan time.Time // 超时单向通道
    tasks     []func(int)      // 存储方法切片
}

//超时返回信息
var ErrTimeout = errors.New("received timeout")
//系统中终止返回信息
var ErrInterrupt = errors.New("received Interrupt")

//Runner生成方法
// os.Signal 系统信息接口
// func After(d Duration) <-chan Time After会在另一线程经过时间段d后向返回值发送当时的时间
func New(d time.Duration) *Runner {
    return &Runner{
        interrupt: make(chan os.Signal, 1),
        complete:  make(chan error),
        timeout:   time.After(d),
    }
}

//向runner中添加需要运行的方法
func (r *Runner) Add(tasks ...func(int)) {
    r.tasks = append(r.tasks, tasks...)
}

//执行添加的每一个任务
func (r *Runner) run() error {
    for id, task := range r.tasks {
        if r.gotInterrupt() {
            return ErrInterrupt
        }
        task(id)
    }

    return nil
}

//验证是否收到系统中断信号
func (r *Runner) gotInterrupt() bool {
    select {
    case <-r.interrupt:
        signal.Stop(r.interrupt)
        return true

    default:
        return false
    }
}

// 执行所有任务,监听通道
// func Notify(c chan<- os.Signal, sig ...os.Signal)
// Notify函数让signal包将输入信号转发到c。如果没有列出要传递的信号,会将所有输入信号传递到c;否则只传递列出的输入信号。
func (r *Runner) Start() error {
    signal.Notify(r.interrupt, os.Interrupt)

    go func() {
        r.complete <- r.run()
    }()

    select {
    case err := <-r.complete:
        return err
    case <-r.timeout:
        return ErrTimeout
    }
}

const timeout = 3 * time.Second

func main() {
    log.Println("starting work")

    r := New(timeout)
    r.Add(createWork(), createWork(), createWork(), createWork(), createWork(), createWork())
    if err := r.Start(); err != nil {
        switch err {
        case ErrInterrupt:
            log.Println("Terminating due to interrupt")
            os.Exit(1)

        case ErrTimeout:
            log.Println("Terminating due to timeout")
            os.Exit(2)
        }
    }

    log.Println("run end!")
}

func createWork() func(int) {
    return func(d int) {
        log.Printf("work: %d start", d)
        time.Sleep(time.Duration(d) * time.Second)
        log.Printf("work %d end", d)
    }
}