当你希望控制一个协程的执行时间,如果超过指定时间,还没有执行完,则退出.直接返回超时错误,这个该如何做呢?
通行做法是用select + channel来进行超时控制,
channel发执行完毕的信号,然后超时信号通用ctx. Done()或者time. After(), 或者time. Ticket()来完成超时通知退出,select捕获到其中一个channel有数据,就执行对应的代码,然后退出.
其中且个注意的点是, channel要用有缓冲的,不然,在超时分支退出时,协程还在卡住,造成goroutine泄露.
代码示例如下
package main
import (
"context"
"fmt"
"sync"
"time"
)
func Do(ctx context.Context, wg *sync.WaitGroup) {
ctx, cancle := context.WithTimeout(ctx, time.Second*2)
defer func() {
cancle()
wg.Done()
}()
done := make(chan struct{}, 1) //执行成功的channel
go func(ctx context.Context) {
fmt.Println("go goroutine")
time.Sleep(time.Second * 10)
done <- struct{}{} //发送完成的信号
}(ctx)
select {
case <-ctx.Done(): //超时
fmt.Printf("timeout,err:%v\n", ctx.Err())
case <-time.After(3 * time.Second): //超时第二种方法
fmt.Printf("after 1 sec.")
case <-done: //程序正常结束
fmt.Println("done")
}
}
func main() {
fmt.Println("main")
ctx := context.Background()
var wg sync.WaitGroup
wg.Add(1)
Do(ctx, &wg)
wg.Wait()
fmt.Println("finish")
}
输出如下:
main
go goroutine
timeout,err:context deadline exceeded
finish
程序执行了超时退出