# 5.8 上下文
从 Go 语言本身的设计来看,尽管我们能够轻松的创建一个 Goroutine, 但对一个已经启动的 Goroutine 做取消操作却并不容易,例如:
```
go func() {
// 如何从其他 Goroutine 通知并结束该 Goroutine 呢?
// ...
}()
```
通过 Channel 与 Select 这一过程间通信原语,我们可以使用空结构信号`struct{}`来通知一个正在执行的 Goroutine:
```
cancel := make(chan struct{})
go func() {
done := make(chan struct{}, 1)
go func() {
defer func() {
done <- struct{}{}
}()
do() // 执行需要执行的操作
}()
select {
case <-cancel:
// 如果提前被取消,则等待执行完毕
// 并撤销已经执行的操作
<-done
undo()
case <-done:
// 如果顺利结束,则结束执行
close(done)
}
}
// ... 出于某些原因希望执行取消操作
cancel <- struct{}{}
```
这样的要求很常见,例如某个 Web 请求被中断,服务端正在请求的资源需要做取消操作等等。那我们能否将这一同步模式进一步抽象为接口,作为一种基于通信的同步模式呢?上下文 Context 包就提供了这样一组在 Goroutine 间进行值传播的方法。
## 上下文接口
```
type Context interface {
// 截止日期返回应取消代表该上下文完成的工作的时间。如果未设置截止日期,则截止日期返回ok == false。连续调用Deadline会返回相同的结果。
Deadline() (deadline time.Time, ok bool)
// Done 返回一个 channel,当代表该上下文完成的工作应被取消时,该通道将关闭。
// 如果此上下文永远无法取消,则可能会返回 nil。
// 连续调用 Done 将返回相同的值。在取消函数返回之后,完成 channel 的关闭可能会异步发生。
Done() <-chan struct{}
// 如果 Done 未被关闭,则 Err 返回 nil;
// 如果 Done 已被关闭,则 Err 返回一个非空错误。
Err() error
// Value 返回了与当前上下文使用 key 相关联的值;
// 没有关联的 key 时将返回 nil。
Value(key interface{}) interface{}
}
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} } var Canceled = errors.New(“context canceled”) var DeadlineExceeded error = deadlineExceededError{} type CancelFunc func()
func Background() Context func TODO() Context func WithCancel(parent Context) (ctx Context, cancel CancelFunc) func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) func WithValue(parent Context, key, val interface{}) Context
```
## 上下文及其衍生品
ctx := context.Background() ctx.WithTimeout(time.Second) ctx.WithDeadline(time.Now()) ctx.WithValue(k, v) ctx.Cancel()
[https://github.com/golang/go/issues/14660](https://github.com/golang/go/issues/14660)[https://github.com/atdiar/goroutine/tree/master/execution](https://github.com/atdiar/goroutine/tree/master/execution)[https://groups.google.com/forum/#](https://groups.google.com/forum/#)!searchin/golang-dev/context$20package|sort:date/golang-dev/JgnR5hrDCu0/pyqbkYfSCQAJ[https://github.com/golang/go/issues/16209](https://github.com/golang/go/issues/16209)[https://github.com/golang/go/issues/8082](https://github.com/golang/go/issues/8082)[https://dave.cheney.net/2017/08/20/context-isnt-for-cancellation](https://dave.cheney.net/2017/08/20/context-isnt-for-cancellation)[https://github.com/golang/go/issues/21355](https://github.com/golang/go/issues/21355)[https://github.com/golang/go/issues/29011](https://github.com/golang/go/issues/29011)[https://github.com/golang/go/issues/28342](https://github.com/golang/go/issues/28342)[https://blog.labix.org/2011/10/09/death-of-goroutines-under-control](https://blog.labix.org/2011/10/09/death-of-goroutines-under-control)[https://godoc.org/gopkg.in/tomb.v2](https://godoc.org/gopkg.in/tomb.v2)[https://blog.golang.org/context](https://blog.golang.org/context)[https://zhuanlan.zhihu.com/p/26695984](https://zhuanlan.zhihu.com/p/26695984)[https://www.flysnow.org/2017/05/12/go-in-action-go-context.html](https://www.flysnow.org/2017/05/12/go-in-action-go-context.html)[https://juejin.im/post/5a6873fef265da3e317e55b6](https://juejin.im/post/5a6873fef265da3e317e55b6)[https://cloud.tencent.com/developer/section/1140703](https://cloud.tencent.com/developer/section/1140703)[https://siadat.github.io/post/context](https://siadat.github.io/post/context)[https://rakyll.org/leakingctx/](https://rakyll.org/leakingctx/)[https://dreamerjonson.com/2019/05/09/golang-73-context/index.html](https://dreamerjonson.com/2019/05/09/golang-73-context/index.html)[https://brantou.github.io/2017/05/19/go-concurrency-patterns-context/](https://brantou.github.io/2017/05/19/go-concurrency-patterns-context/)[http://p.agnihotry.com/post/understanding\_the\_context\_package\_in\_golang/](http://p.agnihotry.com/post/understanding_the_context_package_in_golang/)[https://faiface.github.io/post/context-should-go-away-go2/](https://faiface.github.io/post/context-should-go-away-go2/)[https://juejin.im/post/5c1514c86fb9a049b82a5acb](https://juejin.im/post/5c1514c86fb9a049b82a5acb)[https://segmentfault.com/a/1190000017394302](https://segmentfault.com/a/1190000017394302)[https://36kr.com/p/5073181](https://36kr.com/p/5073181)[https://zhuanlan.zhihu.com/p/60180409](https://zhuanlan.zhihu.com/p/60180409)
- 第一部分 :基础篇
- 第1章 Go语言的前世今生
- 1.2 Go语言综述
- 1.3 顺序进程通讯
- 1.4 Plan9汇编语言
- 第2章 程序生命周期
- 2.1 从go命令谈起
- 2.2 Go程序编译流程
- 2.3 Go 程序启动引导
- 2.4 主Goroutine的生与死
- 第3 章 语言核心
- 3.1 数组.切片与字符串
- 3.2 散列表
- 3.3 函数调用
- 3.4 延迟语句
- 3.5 恐慌与恢复内建函数
- 3.6 通信原语
- 3.7 接口
- 3.8 运行时类型系统
- 3.9 类型别名
- 3.10 进一步阅读的参考文献
- 第4章 错误
- 4.1 问题的演化
- 4.2 错误值检查
- 4.3 错误格式与上下文
- 4.4 错误语义
- 4.5 错误处理的未来
- 4.6 进一步阅读的参考文献
- 第5章 同步模式
- 5.1 共享内存式同步模式
- 5.2 互斥锁
- 5.3 原子操作
- 5.4 条件变量
- 5.5 同步组
- 5.6 缓存池
- 5.7 并发安全散列表
- 5.8 上下文
- 5.9 内存一致模型
- 5.10 进一步阅读的文献参考
- 第二部分 运行时篇
- 第6章 并发调度
- 6.1 随机调度的基本概念
- 6.2 工作窃取式调度
- 6.3 MPG模型与并发调度单
- 6.4 调度循环
- 6.5 线程管理
- 6.6 信号处理机制
- 6.7 执行栈管理
- 6.8 协作与抢占
- 6.9 系统监控
- 6.10 网络轮询器
- 6.11 计时器
- 6.12 非均匀访存下的调度模型
- 6.13 进一步阅读的参考文献
- 第7章 内存分配
- 7.1 设计原则
- 7.2 组件
- 7.3 初始化
- 7.4 大对象分配
- 7.5 小对象分配
- 7.6 微对象分配
- 7.7 页分配器
- 7.8 内存统计
- 第8章 垃圾回收
- 8.1 垃圾回收的基本想法
- 8.2 写屏幕技术
- 8.3 调步模型与强弱触发边界
- 8.4 扫描标记与标记辅助
- 8.5 免清扫式位图技术
- 8.6 前进保障与终止检测
- 8.7 安全点分析
- 8.8 分代假设与代际回收
- 8.9 请求假设与实务制导回收
- 8.10 终结器
- 8.11 过去,现在与未来
- 8.12 垃圾回收统一理论
- 8.13 进一步阅读的参考文献
- 第三部分 工具链篇
- 第9章 代码分析
- 9.1 死锁检测
- 9.2 竞争检测
- 9.3 性能追踪
- 9.4 代码测试
- 9.5 基准测试
- 9.6 运行时统计量
- 9.7 语言服务协议
- 第10章 依赖管理
- 10.1 依赖管理的难点
- 10.2 语义化版本管理
- 10.3 最小版本选择算法
- 10.4 Vgo 与dep之争
- 第12章 泛型
- 12.1 泛型设计的演进
- 12.2 基于合约的泛型
- 12.3 类型检查技术
- 12.4 泛型的未来
- 12.5 进一步阅读的的参考文献
- 第13章 编译技术
- 13.1 词法与文法
- 13.2 中间表示
- 13.3 优化器
- 13.4 指针检查器
- 13.5 逃逸分析
- 13.6 自举
- 13.7 链接器
- 13.8 汇编器
- 13.9 调用规约
- 13.10 cgo与系统调用
- 结束语: Go去向何方?