Go context.WithCancel 的使用(3)
Go context.WithCancel 的使用
当调用cancelFunc()时,会有一步close(d)的操作,
ctx.Done 获取一个只读的 channel,类型为结构体。可用于监听当前 channel 是否已经被关闭。
Done()用来监听cancel操作(对于cancelCtx)或超时操作(对于timerCtx),当执行取消操作或超时时,c.done会被close,这样就能从一个已经关闭的channel里一直获取对应的零值,<-ctx.Done便不会再阻塞
(代码基于go 1.16; 1.17有所改动)
func (c *cancelCtx) Done() <-chan struct{} {
c.mu.Lock()
if c.done == nil {
c.done = make(chan struct{})
}
d := c.done
c.mu.Unlock()
return d
}
func (c *cancelCtx) Err() error {
c.mu.Lock()
err := c.err
c.mu.Unlock()
return err
}
总结一下:使用context.WithCancel时,除了返回一个新的context.Context(上下文),还会返回一个cancelFunc。 在需要取消该context.Context时,就调用这个cancelFunc,之后当前上下文及其子上下文都会被取消,所有的 Goroutine 都会同步收到这一取消信号
至于cancelFunc是如何做到的?
在用户代码,for循环里select不断尝试从 <-ctx.Done()里读取出内容,但此时并没有任何给 c.done这个channel写入数据的操作,(类似c.done <- struct{}{}),故而在for循环里每次select时,这个case都不满足条件,一直阻塞着。每次都执行default代码段
而在执行cancelFunc时, 在func (c *cancelCtx) cancel(removeFromParent bool, err error)里面,会有一个close(c.done)的操作。而从一个已经关闭的channel里可以一直获取对应的零值,即 select可以命中,进入case res := <-ctx.Done():代码段
可用如下代码验证:
package main
import (
"context"
"fmt"
"time"
)
func dosomething(ctx context.Context) {
var cuiChan = make(chan struct{})
go func() {
cuiChan <- struct{}{}
}()
//close(cuiChan)
for {
select {
case res := <-ctx.Done():
fmt.Println("res:", res)
return
case res2 := <-cuiChan:
fmt.Println("res2:", res2)
default:
fmt.Println("I am working!")
time.Sleep(time.Second)
}
}
}
func main() {
test()
ctx, cancelFunc := context.WithCancel(context.Background())
go func() {
time.Sleep(5 * time.Second)
cancelFunc()
}()
dosomething(ctx)
}
func test() {
var testChan = make(chan struct{})
if testChan == nil {
fmt.Println("make(chan struct{})后为nil")
} else {
fmt.Println("make(chan struct{})后不为nil!!!")
}
}
输出:
make(chan struct{})后不为nil!!!
I am working!
res2: {}
I am working!
I am working!
I am working!
I am working!
res: {}
而如果 不向没有缓存的cuiChan写入数据,直接close,即
package main
import (
"context"
"fmt"
"time"
)
func dosomething(ctx context.Context) {
var cuiChan = make(chan struct{})
//go func() {
// cuiChan <- struct{}{}
//}()
close(cuiChan)
for {
select {
case res := <-ctx.Done():
fmt.Println("res:", res)
return
case res2 := <-cuiChan:
fmt.Println("res2:", res2)
default:
fmt.Println("I am working!")
time.Sleep(time.Second)
}
}
}
func main() {
test()
ctx, cancelFunc := context.WithCancel(context.Background())
go func() {
time.Sleep(5 * time.Second)
cancelFunc()
}()
dosomething(ctx)
}
func test() {
var testChan = make(chan struct{})
if testChan == nil {
fmt.Println("make(chan struct{})后为nil")
} else {
fmt.Println("make(chan struct{})后不为nil!!!")
}
}
相关阅读
-
开网站需要多少钱 公司网页制作流程及费用
一篇很详细的教程是关于开网站需要多少钱和公司网页制作流程及费用的相关经验,接下来IT袋带大家一起了解。 这个问题可以说是网站建设常见问题最热门的问题,也是客户咨询的最多的问
-
简单的网站设计模板 一个完整的网页设计流程
本文导读:简单的网站设计模板和一个完整的网页设计流程的电脑小知识,一起来看看吧! 随着个人创业的流行,很多个人也需要一个比较详细的网站来展示自己,开展个人业务,或者积累粉
-
哔哩哔哩是网站吗 哔哩哔哩是个什么网站
小编为你解答哔哩哔哩是个什么网站的IT小经验,哔哩哔哩是中国年轻世代高度聚集的文化社区和视频网站,简称B站,B站早期是一个ACG(动画、漫画、游戏)内容创作与分享的视频网站。
-
python工具有哪些 新手python编程入门自学知识
为大家分享python工具有哪些和新手python编程入门自学知识的介绍,如有不对的地方欢迎指正! 随着python的火热,不少的程序员业余时间都会研究这门编程语言。 利用python开发,大牛用vim,接了


