用go来实现一个规定时间内跑完几个函数

学习了一个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)
                }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值