context.WithCancel()取消机制的理解
Golang中context包提供上下文机制在 goroutine 之间传递 deadline、取消信号(cancellation signals)或者其他请求相关的信息。
其中context.WithCancel 函数能够从 context.Context 中衍生出一个新的子上下文并返回用于取消该上下文的函数。一旦我们执行返回的取消函数,当前上下文以及它的子上下文都会被取消,所有的 Goroutine 都会同步收到这一取消信号。
先来看一个简单的demo,父母在家小娃就得学习,小娃每隔1s告诉他父母 “I am working!”,但是过了5s后父母出门了,小娃就没必要学习了,于是开始 “playing”
这里基于根上下文context.Background(),生成了一个带取消函数cancelFunc的子上下文ctx(可以理解为是:父母在家,小娃基于父母在家这个前提一直在学习),通过调用这个cancelFunc,释放一个信号(父母出门)结束基于这个子上下文的工作(还学个甚),也就是从ctx.Done()这个channel获取到值,执行了return操作。
那么今天主要就是理解下为什么调用了cancelFunc就能从ctx.Done()里取得返回值
cancel()机制
首先提下非常关键的一点,今天要研究的这个机制,充分利用了Golang语言channel的一个特性:
从一个已经关闭的channel里可以一直获取对应的零值
可以看到当传入的01234依次被取出后依旧取出了int类型的零值
回到context,先看下WithCancel函数的定义,最后返回的是子上下文和一个cancelFunc函数,而cancelFunc函数里调用了cancelCtx这个结构体的方法cancel
注意到cancelCtx这个结构体,字段done是一个传递空结构体类型的channel,用来在上下文取消时关闭这个通道,err就是在上下文被取消时告诉用户这个上下文取消了,可以用ctx.Err()来获取信息
从上面的代码close(c.done)就可以看到通过执行cancel函数将c.done通道关闭了,也就是demo里的ctx.Done()通道(ps:可以简单看下Done方法的定义)
总结
1、从一个被close的channel中接收数据不会被阻塞,而是立即返回,接收完已发送的数据后会返回传递的元素类型的零值(zero value)
2、ctx, cancelFunc := context.WithCancel(context.Background()) 通过执行cancelFunc函数关闭chan struct{}通道,从而可以从ctx.Done()获取返回值{},最后结束该上下文