在 Go 语言中,context
包提供了ctx
(上下文)和cancel
相关的函数,用于管理多个goroutine
的生命周期和传递截止日期、取消信号等信息。以下是一些常用的相关函数:
1.context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
- 功能:创建一个可取消的上下文
ctx
,以及一个取消函数cancel
。当调用cancel
函数时,与该ctx
关联的所有goroutine
都会收到取消信号,从而可以停止执行。
2.context.WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancel CancelFunc)
- 功能:创建一个带有超时时间的上下文
ctx
和取消函数cancel
。timeout
参数指定了从创建上下文开始的最长持续时间。当超过这个时间后,上下文会自动取消,与该ctx
关联的所有goroutine
都会收到取消信号。
3.context.WithDeadline(parent Context, d time.Time) (ctx Context, cancel CancelFunc)
- 功能:创建一个带有绝对截止时间
d
的上下文ctx
和取消函数cancel
。当到达指定的截止时间d
时,上下文会自动取消,关联的goroutine
会收到取消信号。
这些函数在处理并发编程时非常有用,特别是在需要控制goroutine
的生命周期、处理超时和取消操作的场景中。通过使用上下文,你可以确保在程序的不同部分之间有效地传递和管理这些信息。
使用示例
我们以WithCancel函数为例,看看具体怎么使用的:
1 | package main |
执行结果为:
1 | ➜ my go run main.go |
解释一下为什么会出现这种情况:
首先创建了一个可以取消的ctx,起了两个goroutine,然后主线程休眠3秒,所以“任务正在运行”和“hello”输出了三次。
这时候执行cancel,ctx就有error了,所以会打印出“context canceled”。好处是多次cancel也没啥问题。因为这里cancel了两次,打印了两次错误。
当执行完cancel后,第一个goroutine监听了ctx.Done,立即退出,所以显示“任务被取消”。但是第二个不感知,所以继续执行。
上下文结构
Go 语言的上下文是有层级关系的树状结构。一个父上下文取消时,会级联取消所有子上下文。
1 | package main |
输出结果为:
1 | ➜ my go run main.go |
可以看到,到1s的时候,父子上下文的Done都被唤起了。
现实使用
客户端主动取消
客户端程序
1 | package main |
服务端程序
1 | package main |
执行结果
1 | ➜ my go run main.go |
可以看到,客户端10s的时候取消了请求,所以“客户端请求”执行了10次,取消之后,打印“客户端取消了请求”后便不再执行。但因为“默默执行”的goroutine没有关注ctx.done,所以即使已经finish了,goroutine仍然会继续执行。
服务端主动停止所有任务
1 | package main |
执行结果
1 | ➜ my go run main.go |