上篇文章中,我们已经学习了使用context实现控制多个goroutine的退出。
本文将继续介绍如何使用context实现超时情况下,让多个goroutine退出。
例子
首先,启动3个goroutine,分别为1,2,3。这3个goroutine又分别启动一个goroutine。
一共有6个goroutine,它们之间关系如下:
- 1
- 11
- 2
- 21
- 3
- 31
task1,2,3被限制在指定时间内结束,即50ms。
如果不能按时结束,会触发超时操作,这样,task 1、2、3接收到超时信号,取消自己启动的goroutine(即task11,21,31),然后退出。
最后,所有的goroutine都退出了。
代码如下:
ackage main
import (
"context"
"fmt"
"time"
)
func PrintTask(ctx context.Context, taskName string) {
for {
select {
case <- ctx.Done():
fmt.Println("task:", taskName, "ctx error:", ctx.Err()," then task exit...")
return
default:
time.Sleep(1*time.Second)
fmt.Println("task:", taskName, " doing something...")
}
}
}
func mainTask(ctx context.Context, taskName string) {
ctx1, cancel := context.WithCancel(ctx)
defer cancel()
// create a new task
newTaskName := taskName + "1"
go PrintTask(ctx1, newTaskName)
for {
select {
case <- ctx.Done():
fmt.Println("task:", taskName, "ctx error:", ctx.Err()," then task exit...")
return
default:
time.Sleep(1*time.Second)
fmt.Println("task:", taskName, " doing something...")
}
}
}
func main() {
ctx := context.Background()
timeout := 50*time.Millisecond
ctx, _ = context.WithTimeout(ctx, timeout)
go mainTask(ctx, "1")
go mainTask(ctx, "2")
go mainTask(ctx, "3")
select{
case <-ctx.Done():
// timeout
fmt.Println("main task error:", ctx.Err())
}
fmt.Println("main exit...")
time.Sleep(3*time.Second)
}
输出
main task error: context deadline exceeded
main exit...
task: 21 doing something...
task: 1 doing something...
task: 21 ctx error: context deadline exceeded then task exit...
task: 3 doing something...
task: 3 ctx error: context deadline exceeded then task exit...
task: 1 ctx error: context deadline exceeded then task exit...
task: 11 doing something...
task: 11 ctx error: context deadline exceeded then task exit...
task: 31 doing something...
task: 2 doing something...
task: 31 ctx error: context deadline exceeded then task exit...
task: 2 ctx error: context deadline exceeded then task exit...