Golang的context包用于在goroutine间传递取消信号、截止时间和请求数据,核心是通过context.Context接口及WithCancel、WithDeadline、WithValue等函数实现上下文控制。

Golang的context包主要用于在goroutine之间传递取消信号、截止时间以及请求相关的值。它允许你控制goroutine的生命周期,确保资源得到有效管理,尤其是在处理并发请求时。
解决方案
Context的核心在于其接口和几个关键的实现:
context.Context
context.Background()
context.TODO()
context.WithCancel()
context.WithDeadline()
context.WithValue()
传递取消信号: 使用
context.WithCancel()
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Worker: 任务取消")
return
default:
fmt.Println("Worker: 执行任务...")
time.Sleep(time.Second)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
time.Sleep(3 * time.Second)
fmt.Println("Main: 取消任务")
cancel()
time.Sleep(time.Second) // 等待worker退出
fmt.Println("Main: 结束")
}在这个例子中,
context.WithCancel
worker
ctx.Done()
cancel()
worker
传递截止时间: 使用
context.WithDeadline()
context.WithTimeout()
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
deadline, ok := ctx.Deadline()
if ok {
fmt.Printf("Worker: 截止时间 %v\n", deadline)
}
for {
select {
case <-ctx.Done():
fmt.Println("Worker: 任务超时或取消")
return
default:
fmt.Println("Worker: 执行任务...")
time.Sleep(time.Second)
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // 确保在main函数退出时取消context,释放资源
go worker(ctx)
time.Sleep(3 * time.Second)
fmt.Println("Main: 结束")
}这里,
context.WithTimeout
cancel()
worker
defer cancel()
传递值: 虽然主要用于取消和截止时间,
context.WithValue()
package main
import (
"context"
"fmt"
)
func worker(ctx context.Context) {
userID := ctx.Value("userID")
if userID != nil {
fmt.Printf("Worker: userID = %v\n", userID)
} else {
fmt.Println("Worker: userID 未找到")
}
}
func main() {
ctx := context.WithValue(context.Background(), "userID", "12345")
go worker(ctx)
fmt.Println("Main: 结束")
}在这个例子中,
context.WithValue
worker
如何避免Context泄露?
Context泄露指的是Context被传递给goroutine,但goroutine并没有在适当的时候退出,导致资源无法释放。这通常发生在goroutine持续运行,即使其结果不再需要,或者Context的父Context已经取消。
使用defer取消Context: 当使用
context.WithTimeout
context.WithDeadline
defer cancel()
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// ...
}确保goroutine监听Done()通道: 所有接收Context的goroutine都应该监听
ctx.Done()
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
// 清理资源并退出
return
default:
// 执行任务
}
}
}避免无限期阻塞的goroutine: 确保goroutine不会因为某些操作(如等待channel)而无限期阻塞。可以使用
select
ctx.Done()
func worker(ctx context.Context, ch <-chan int) {
for {
select {
case <-ctx.Done():
return
case val := <-ch:
// 处理val
case <-time.After(time.Second):
// 超时处理
}
}
}避免在循环中创建Context: 如果需要在循环中创建Context,确保每次循环都创建一个新的Context,而不是重复使用同一个Context。
for i := 0; i < 10; i++ {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
go func(ctx context.Context, i int) {
defer cancel()
// ...
}(ctx, i)
}使用Context传递必要信息: 避免使用Context传递大量数据。Context主要用于传递取消信号、截止时间和少量请求相关的值。对于大量数据,应使用函数参数或其他更合适的方式传递。
Context的Value应该放什么?
context.Value
context.Value
适合放置的内容:
context.WithDeadline
不适合放置的内容:
如何测试使用了Context的代码?
测试使用Context的代码需要模拟Context的行为,例如取消信号和截止时间。
使用context.Background()
context.TODO()
context.Background()
context.TODO()
context.WithCancel
context.WithTimeout
context.WithValue
func TestWorker(t *testing.T) {
ctx := context.Background()
// ...
}模拟取消信号: 可以使用
context.WithCancel
func TestWorkerCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go worker(ctx)
// 模拟取消信号
cancel()
// 等待goroutine退出
time.Sleep(time.Millisecond * 100)
// 验证结果
// ...
}模拟截止时间: 可以使用
context.WithTimeout
func TestWorkerTimeout(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)
defer cancel()
go worker(ctx)
// 等待超时
time.Sleep(time.Millisecond * 200)
// 验证结果
// ...
}验证Context传递的值: 可以使用
ctx.Value()
func TestWorkerValue(t *testing.T) {
ctx := context.WithValue(context.Background(), "userID", "123")
go worker(ctx)
// 等待goroutine执行
time.Sleep(time.Millisecond * 100)
// 验证结果
// ...
}使用testing.T
Cleanup
testing.T
Cleanup
func TestWorker(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
go worker(ctx)
// ...
}通过这些方法,可以有效地测试使用了Context的代码,确保其在各种情况下都能正确运行。
以上就是Golang的context包如何在goroutine间传递取消信号和截止时间的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号