y

ywanbing

V1

2022/04/12阅读:17主题:默认主题

chan 原理一个流程图搞定

通过chan的流程图,流程化的看源码,事半功倍。

步骤:

  1. 先看阻塞的流程,也就是剔除绿色虚线的部分
  2. 完整看完流程后,再进行非阻塞的流程。
  3. 最后查看他们之间的关系

chan的源码位置在runtime/chan.go 中,其中主要的方法有下面几个:

  • makechan(t *chantype, size int) *hchan
  • chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool
  • chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
  • selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool)
  • selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool)
  • closechan(c *hchan)

在没有看源码之前,一直没有太关注chan的非阻塞调用怎么实现的;但是一看源码,就会发现它实现的非常巧妙。

func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {
 return chansend(c, elem, false, getcallerpc())
}

...

func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool) {
 return chanrecv(c, elem, false)
}

可以看到,通过一个bool 变量区分,再通过上层的不同函数来传入不同的参数。

对此有一个疑问,什么时候会用到非阻塞的chan调用呢?

我们可以看到,有编译器实现优化:

selectnbsend:

// compiler implements
select {
 case c <- v:
  ... foo
 default:
 ... bar
}

// as
if selectnbsend(c, v) {
 ... foo
else {
 ... bar
}

selectnbrecv:

// compiler implements
select {
case v, ok = <-c:
 ... foo
default:
 ... bar
}

// as
if selected, ok = selectnbrecv(&v, c); selected {
 ... foo
else {
 ... bar
}

具体的实现细节就需要自己通过源码来进行细节分析了。

由于高清大图,上传不到,请复制链接查看,或者点击查看原文。

https://github.com/ywanbing/golearning/blob/master/chan_flow_chart/chan_flow_chart.png

关注订阅号:

GolangNewbie GO菜鸟

学习更多!

分类:

后端

标签:

Golang

作者介绍

y
ywanbing
V1