1 runner/runner.go
package runner
import ("errors"
"os"
"os/signal"
"time"
)
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")
//
func New(d time.Duration) *Runner {
return &Runner {
interrupt:make(chan os.Signal, 1), //创建信号通道
complete: make(chan error), //任务完成通道
timeout:time.After(d), //返回一个时间通道,该通道在无操作时,d时间后激发超时
}
}
func (r *Runner) Add(tasks ...func(int)) { //添加任务,可以多参数方式,可以添加多个。
r.tasks = append(r.tasks,tasks...) //切片可以这样使用。数组不可以使用
}
func (r* Runner) Start() error {
signal.Notify(r.interrupt, os.Interrupt) //信号接收通知
go func(){
r.complete <- r.run() //启动另外一个线程,把所有任务都运行一遍,把错误返回,1, nil正常,2 ErrInterrupt
}()
select { //如果有通道接收到消息,就运行。如果没有通道接收到消息就阻塞。此时主线程就阻塞,等待信号到来。
case err := <-r.complete: //等待到任务完成
return err
case <-r.timeout: //等待到操作
return ErrTimeout
}
}
func (r *Runner) run() error {
for id, task := range r.tasks { //遍历数组,运行各个函数
if r.gotInterrupt() {
return ErrInterrupt //接收到CTRL-C信号
}
task(id)
}
return nil //如果无误,则返回nil
}
func (r *Runner) gotInterrupt() bool {
select {
case <- r.interrupt: //接收到os.Interrupt
signal.Stop(r.interrupt) //停止接收以后的信号
return true
default:
return false
}
}
2 main/main.go
package mainimport (
"ch7/runner"
"log"
"time"
"os"
)
const timeout = 5 * time.Second
func main() {
log.Println("Starting work.")
r := runner.New(timeout)
r.Add(createTask(), createTask(),createTask())
if err := r.Start(); err != nil {
switch err {
case runner.ErrTimeout:
log.Println("Terminating due to timeout.")
os.Exit(1)
case runner.ErrInterrupt:
log.Println("Terminating due to interrupt.")
os.Exit(2)
}
}
log.Println("Process ended.")
}
func createTask() func(int){
return func(id int){
log.Printf("Processor - Task #%d.", id)
time.Sleep(time.Duration(id)*time.Second)
}
}