当你希望控制一个协程的执行时间,如果超过指定时间,还没有执行完,则退出.直接返回超时错误,这个该如何做呢?

通行做法是用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

程序执行了超时退出