首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
本小册内容介绍
本小册内容综述
Go语言简介:历史背景、发展现状及语言特性
编写第一个Go程序
变量、常量以及与其他语言的差异
数据类型
运算符
条件和循环
数组和切片
Map声明、元素访问及遍历
Map与工厂模式,在Go语言中实现Set
字符串
Go语言的函数
可变参数和defer
行为的定义和实现
Go语言的相关接口
扩展与复用
不一样的接口类型,一样的多态
编写好的错误处理
panic和recover
构建可复用的模块(包)
依赖管理
协程机制
共享内存并发机制
CSP并发机制
多路选择和超时
channel的关闭和广播
任务的取消
Context与任务取消
只运行一次
仅需任意任务完成
所有任务完成
对象池
sync.pool对象缓存
单元测试
Benchmark
BDD
反射编程
万能程序
不安全编程
实现pipe-filter framework
实现micro-kernel framework
内置JSON解析
easyjson
HTTP服务
构建RESTful服务
性能分析工具
性能调优示例
别让性能被锁住
GC友好的代码
高效字符串连接
面向错误的设计
面向恢复的设计
Chaos Engineering
当前位置:
首页>>
技术小册>>
Go语言从入门到实战
小册名称:Go语言从入门到实战
### 章节:Context与任务取消 在Go语言的并发编程模型中,`Context` 是一个非常关键且强大的概念,它用于在不同goroutine之间传递截止日期、取消信号以及其他请求范围内的值。通过`Context`,Go程序能够优雅地处理超时、取消操作以及传递跨API边界的请求特定数据,从而构建出更加健壮、可维护的并发系统。本章将深入探讨`Context`接口的定义、使用场景、以及如何通过`Context`实现任务的取消。 #### 一、Context 接口概览 在Go标准库中,`context` 包定义了一个`Context`接口,该接口包含四个方法: - `Deadline() (deadline time.Time, ok bool)`: 返回一个表示截止时间的`time.Time`和一个布尔值,如果设置了截止时间,`ok`为`true`;否则返回`ok`为`false`。注意,即使`ok`为`false`,也不代表`Context`不能被取消。 - `Done() <-chan struct{}`: 返回一个只读的`chan struct{}`,当`Context`被取消或截止时间到达时,该通道会被关闭。 - `Err() error`: 返回`Context`被取消或结束的原因,只有在`Done`通道被关闭后调用此方法才有意义。 - `Value(key interface{}) interface{}`: 根据给定的键返回存储在`Context`中的值,用于传递跨API边界的请求特定数据。 #### 二、Context 的实现与传递 Go标准库提供了几种基础的`Context`实现,包括`Background`、`TODO`、`WithCancel`、`WithDeadline`、`WithTimeout`和`WithValue`。 - **Background** 和 **TODO**:这两个函数返回的是非空的`Context`实例,通常用于初始化最顶层的`Context`,或在不清楚使用哪个`Context`时作为默认选项。它们之间没有明显的区别,但在实际编码中,推荐使用`Background`作为所有全局`Context`的起点,而`TODO`用于尚不确定使用哪种`Context`时的占位符。 - **WithCancel**:创建一个新的`Context`,并返回一个取消函数。调用取消函数将关闭返回的`Context`的`Done`通道,表示操作被取消。 - **WithDeadline** 和 **WithTimeout**:这两个函数分别用于设置`Context`的截止时间或超时时间。如果超过了设定的时间,`Context`的`Done`通道将被关闭,并返回一个超时错误。`WithTimeout`是`WithDeadline`的一个便捷封装,它接受一个超时时间(基于当前时间)。 - **WithValue**:用于在`Context`中存储键值对,这在跨API传递元数据时非常有用。但需要注意的是,存储的值应当是安全的,因为它们可能会被多个goroutine同时访问。 在Go程序中,`Context`应该被显式地从一个函数传递到另一个函数,形成一个`Context`树。每个函数都应该接受一个`Context`作为第一个参数,并在调用其他函数时传递这个`Context`。 #### 三、任务取消的实现 任务取消是`Context`的一个重要应用场景。通过在`Context`中传递取消信号,我们可以安全地中断长时间运行的操作或等待中的操作,从而避免资源的浪费或死锁。 ##### 示例:使用`WithCancel`实现任务取消 ```go package main import ( "context" "fmt" "time" ) func longRunningTask(ctx context.Context) { select { case <-time.After(5 * time.Second): fmt.Println("Task completed successfully") case <-ctx.Done(): fmt.Println("Task cancelled:", ctx.Err()) return } } func main() { ctx, cancel := context.WithCancel(context.Background()) go longRunningTask(ctx) // 假设在2秒后,我们决定取消任务 time.Sleep(2 * time.Second) cancel() // 等待足够长的时间以确保goroutine已经退出 time.Sleep(1 * time.Second) } ``` 在这个例子中,`longRunningTask` 函数模拟了一个长时间运行的任务,它使用`select`语句来监听两个通道:一个是`time.After`返回的通道,表示任务正常完成;另一个是`ctx.Done()`返回的通道,表示任务被取消。通过调用`cancel`函数,主goroutine发送了一个取消信号给`Context`,导致`longRunningTask`中的`select`语句选择了`ctx.Done()`分支,从而提前退出了任务。 #### 四、注意事项与最佳实践 1. **不要将`Context`存储在结构体中**:`Context`应该显式地通过函数参数传递,而不是存储在结构体中。这是因为`Context`的生命周期应与请求或操作的生命周期绑定,而不是与某个对象或结构体绑定。 2. **传递`Context`到所有需要它的函数**:在编写并发程序时,确保所有需要`Context`的函数都接受一个`Context`参数,并在调用这些函数时传递正确的`Context`。 3. **使用`context.TODO()`作为临时占位符**:在尚未确定使用哪个`Context`时,可以使用`context.TODO()`作为占位符。但请记得在后续开发中替换为合适的`Context`。 4. **避免在`Context`中存储大量数据**:虽然`WithValue`允许在`Context`中存储键值对,但应尽量避免存储大量数据或复杂对象,因为这可能会导致内存泄露或不必要的性能开销。 5. **注意`Context`的取消传播**:当你取消一个`Context`时,这个取消信号会沿着`Context`树向下传播。因此,在设计并发系统时,要仔细考虑`Context`的层次结构和取消逻辑。 6. **使用`context.Background()`初始化最顶层的`Context`**:在程序的入口点或全局范围内,使用`context.Background()`初始化最顶层的`Context`。这是所有其他`Context`的起点。 通过理解和应用`Context`与任务取消的机制,Go程序能够更加灵活地处理并发操作,提升程序的健壮性和可维护性。
上一篇:
任务的取消
下一篇:
只运行一次
该分类下的相关小册推荐:
WebRTC音视频开发实战
深入浅出Go语言核心编程(五)
Go Web编程(下)
Golang修炼指南
深入浅出Go语言核心编程(三)
Go开发权威指南(下)
Go语言入门实战经典
深入解析go语言
企业级Go应用开发从零开始
Go开发基础入门
深入浅出Go语言核心编程(四)
Go Web编程(上)