最近看到将go里面context的文章,之前对这块了解不多。感觉很奇怪为何parent context的取消同时可以影响到child context的取消,以此做链式控制。跟着网上前人的一些技术博客看了下context源码,代码不多也很容易看懂。这里记录下看到的几个有意思的地方。
第一个是WithCancel
这个函数返回的时候:
最后返回了一个func(){c.cancel(true, Canceled)}
,这里很巧妙。用一个wrapper方法将实际函数内容返回出去,等到最终调用的时候才执行c.cancel(true, Canceled)
,跟python的装饰器一样。
同样在找到的Go语言并发模型:像Unix Pipe那样使用channel这篇博客里,使用channel实现的’pipline’也很有趣。摘抄一点代码
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
这里将channel返回出来,然后协程里起始在往这个channel里写数据,但是由于channel没有被读取而阻塞。所以当channel被读取的时候,协程里往channel里放数据的操作才会执行。
说回context接口。除了最原始的background
和todo
这两个emptyCtx
类型之外,其余实现context接口的都是以cancelCtx
作为基础。所以每个类型都会存在done
和children
,parent context之所以可以影响到child context就是因为它在children
属性里记录了所有child context;控制通过done
这个cahnnel来实现,在协程中通过调用Done()
函数监听到done
这个channel,然后在cancel
函数被调用的时候关闭掉done
channel,从而将一个信号让所有监听的地方收到;同时还会遍历当前context的’children’,去cancel
掉所有的child context,致使所有监听child context的done
channel的地方收到信号。