想象一下,你正在一家快餐店等待你的订单。你的订单是由多个步骤组成的:首先是下单,然后是制作食物,最后是打包给你。在这个过程中,如果你决定取消订单,你希望快餐店立刻停止制作和打包,以节省时间和资源。这个"取消订单"的动作,就是 context
所做的事情。
context
是 Go 语言在 1.7 版本引入的一个标准库包,主要用来处理超时、取消操作和传递请求相关的值。它是一个为了让程序更加健壮、更容易管理而设计的功能。
context
做什么的?
- 取消信号 :它可以向所有使用同一个
context
的 goroutine 发送取消信号。这在你想要停止正在执行的操作时非常有用,比如说当用户取消了请求,或者操作已经超时了。 - 超时控制 :
context
可以设定一个超时时间,一旦过了这个时间,就会自动发送取消信号。这对于控制那些可能会花费很长时间才能完成的操作特别有用。 - 值传递 :
context
还可以用来传递请求范围内的数据,比如用户的身份信息、授权token等,这样在整个请求链中的任何一个环节都可以访问到这些信息。
怎么用?
使用 context
的典型模式是通过 context.Background()
或 context.TODO()
创建一个根 context
,然后通过 context.WithCancel()
、context.WithDeadline()
、context.WithTimeout()
或 context.WithValue()
来派生出子 context
。
取消信号
go
import (
"context"
"fmt"
"time"
)
func main() {
// 创建一个根context
ctx, cancel := context.WithCancel(context.Background())
// 在一个goroutine中使用这个context
go func() {
time.Sleep(2 * time.Second)
// 2秒后发送取消信号
cancel()
}()
// 等待取消信号
<-ctx.Done()
fmt.Println("Received cancel signal, shutting down...")
}
超时控制
go
package main
import (
"context"
"fmt"
"time"
)
func operation(ctx context.Context) {
for {
select {
case <-time.After(3500 * time.Millisecond): // 模拟耗时的操作
fmt.Println("operation finished")
case <-ctx.Done():
fmt.Println("operation cancelled")
return
}
}
}
func main() {
// 创建一个将在1秒后超时的context
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() // 不要忘记调用cancel
go operation(ctx)
// 等待足够长的时间,以观察操作是否会因为超时而取消
time.Sleep(2 * time.Second)
}
值传递
go
package main
import (
"context"
"fmt"
)
func process(ctx context.Context) {
// 从context中提取值
if v := ctx.Value("key"); v != nil {
fmt.Println("found value:", v)
return
}
fmt.Println("value not found")
}
func main() {
ctx := context.Background()
// 使用context.WithValue来传递值
ctx = context.WithValue(ctx, "key", "this is a value")
process(ctx)
}