context
1、context 结构是什么样的? 2、context 使用场景和用途?
在 Go 语言中,context
是一个标准库提供的结构,用于在多个 Goroutine 之间传递请求的上下文信息。它的主要用途是控制 Goroutine 的生命周期,尤其是在处理超时、取消操作、以及传递请求范围内的元数据时,context
提供了一个简洁且高效的方式。
# 1. context
结构
context
实际上是一个接口,主要有四种方法:
Deadline() (deadline time.Time, ok bool)
:返回context
何时会超时,如果没有设置超时,则ok
为false
。Done() <-chan struct{}
:返回一个 channel,当context
被取消或超时时,该 channel 会被关闭,通知监听方操作已取消。Err() error
:返回context
结束的原因,如果Done
channel 关闭,Err
返回context.Canceled
或context.DeadlineExceeded
。Value(key interface{}) interface{}
:允许在context
中存储和检索与 key 相关联的值,用于传递请求范围内的数据。
func fun1{
// 创建一个新的上下文,并在其中存储用户ID
ctx := context.WithValue(r.Context(), userIDKey, "12345")
// 将新的上下文传递给下游的处理函数
processRequest(ctx, w)
}
func processRequest(ctx context.Context, w http.ResponseWriter)
// 从上下文中获取用户ID
userID := ctx.Value(userIDKey).(string)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 2. context
的使用场景
context
主要用于以下几个场景:
- Goroutine 的取消:当多个 Goroutine 协作时,
context
可以用于在父 Goroutine 取消操作时通知所有子 Goroutine 停止工作。 - 超时控制:在需要设置超时的场景中,例如 HTTP 请求或数据库查询等操作,
context
可以在超时时自动取消操作,避免 Goroutine 长时间阻塞。 - 传递元数据:
context
可以用来传递请求范围内的元数据,比如用户身份信息、授权令牌、跟踪 ID 等。这对于在请求的不同阶段共享数据非常有用。
# 3. 常用的 context
函数
Go 提供了几个用于创建和操作 context
的函数:
context.Background()
:返回一个空的上下文,通常作为根context
使用。context.TODO()
:在不确定要使用哪个context
时使用的占位符,后续可以替换为实际的context
。context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
:返回一个可取消的context
,调用cancel
函数可以取消该context
。context.WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
:返回一个带有截止时间的context
,在指定时间点之后,该context
会自动取消。context.WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
:返回一个带有超时的context
,超时后自动取消。context.WithValue(parent Context, key, val interface{}) Context
:返回一个带有值的context
,可以通过key
取回val
。
# 4. context
的示例
以下是使用 context
控制 Goroutine 取消的示例:
package main
import (
"context"
"fmt"
"time"
)
func main() {
// 创建一个带取消功能的 context
ctx, cancel := context.WithCancel(context.Background())
// 启动一个 Goroutine
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Goroutine 停止")
return
default:
fmt.Println("Goroutine 正在运行")
time.Sleep(500 * time.Millisecond)
}
}
}(ctx)
// 主程序等待一段时间后取消 Goroutine
time.Sleep(2 * time.Second)
cancel()
// 等待 Goroutine 完成
time.Sleep(1 * time.Second)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
在这个例子中,context.WithCancel
创建了一个带取消功能的 context
。当主程序调用 cancel()
时,context.Done()
channel 会关闭,Goroutine 检测到此变化后终止运行。
# 5. context
的最佳实践
- 不要在函数中传递
nil
context
:总是使用context.TODO()
或context.Background()
作为起点。 - 及时调用
cancel
函数:确保使用context.WithCancel
或context.WithTimeout
时,最终会调用返回的CancelFunc
,以防止资源泄漏。 - 在请求生命周期中传递
context
:在处理 HTTP 请求或 RPC 调用时,将context
贯穿整个请求生命周期,以便在需要时控制超时和取消。
context
是 Go 语言并发编程中的一个关键工具,能够有效管理 Goroutine 的生命周期,确保资源的正确释放,并在复杂的并发场景中传递元数据。
编辑 (opens new window)
上次更新: 2024/09/13, 11:59:12