1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如
- channel为什么是并发安全的?
- 同步通道和异步通道有啥区别?
- 通道为何会阻塞协程?
- 使用通道导致阻塞的协程是如何解除阻塞的?
2.原理2.1创建channel理论上有三种,带缓冲不带缓冲nil,写法如下:
// bufferedch := make(chan Task, 3)// unbufferedch := make(chan int)// nilvar ch chan int复制代码
追踪make函数,会发现在builtin/builtin.go中仅有一个声明func make(t Type, size ...IntegerType) Type 。真正的实现可以参考go内置函数make,简单来说在cmd/compile/internal/gc/typecheck.go中有函数typecheck1
// The result of typecheck1 MUST be assigned back to n, e.g.//n.Left = typecheck1(n.Left, top)func typecheck1(n *Node, top int) (res *Node) { if enableTrace && trace {defer tracePrint("typecheck1", n)(&res) } switch n.Op { case OMAKE:ok |= ctxExprargs := n.List.Slice()if len(args) == 0 {yyerror("missing argument to make")n.Type = nilreturn n}n.List.Set(nil)l := args[0]l = typecheck(l, Etype)t := l.Typeif t == nil {n.Type = nilreturn n}i := 1switch t.Etype {default:yyerror("cannot make type %v", t)n.Type = nilreturn ncase TCHAN:l = nilif i < len(args) {l = args[i]i++l = typecheck(l, ctxExpr)l = defaultlit(l, types.Types[TINT])if l.Type == nil {n.Type = nilreturn n}if !checkmake(t, "buffer", l) {n.Type = nilreturn n}n.Left = l} else {n.Left = nodintconst(0)}n.Op = OMAKECHAN //对应的函数位置}if i < len(args) {yyerror("too many arguments to make(%v)", t)n.Op = OMAKEn.Type = nilreturn n}n.Type = tif (top&ctxStmt != 0) && top&(ctxCallee|ctxExpr|Etype) == 0 && ok&ctxStmt == 0 {if !n.Diag() {yyerror("%v evaluated but not used", n)n.SetDiag(true)}n.Type = nilreturn n}return n }}复制代码
最终真正实现位置为runtime/chan.gofunc makechan(t *chantype, size int) *hchan {elem := t.elem// compiler checks this but be safe.if elem.size >= 1<<16 {throw("makechan: invalid channel element type")}if hchanSize%maxAlign != 0 || elem.align > maxAlign {throw("makechan: bad alignment")}mem, overflow := math.MulUintptr(elem.size, uintptr(size))if overflow || mem > maxAlloc-hchanSize || size < 0 {panic(plainError("makechan: size out of range"))}// Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers.// buf points into the same allocation, elemtype is persistent.// SudoG's are referenced from their owning thread so they can't be collected.// TODO(dvyukov,rlh): Rethink when collector can move allocated objects.var c *hchanswitch {case mem == 0:// Queue or element size is zero.c = (*hchan)(mallocgc(hchanSize, nil, true))// Race detector uses this location for synchronization.c.buf = c.raceaddr()case elem.kind&kindNoPointers != 0:// Elements do not contain pointers.// Allocate hchan and buf in one call.c = (*hchan)(mallocgc(hchanSize+mem, nil, true))c.buf = add(unsafe.Pointer(c), hchanSize)default:// Elements contain pointers.c = new(hchan)c.buf = mallocgc(mem, elem, true)}c.elemsize = uint16(elem.size)c.elemtype = elemc.dataqsiz = uint(size)if debugChan {print("makechan: chan=", c, "; elemsize=", elem.size, "; elemalg=", elem.alg, "; dataqsiz=", size, "n")}return c}复制代码
从这个函数可以看出,channel的数据结构为hchan2.2结构接下来我们看一下channel的数据结构,基于数据结构,可以推测出具体实现 。
runtime/chan.go
type hchan struct { //channel队列里面总的数据量 qcountuint// total data in the queue // 循环队列的容量,如果是非缓冲的channel就是0 dataqsiz uint// size of the circular queue // 缓冲队列,数组类型 。bufunsafe.Pointer // points to an array of dataqsiz elements // 元素占用字节的size elemsize uint16 // 当前队列关闭标志位,非零表示关闭 closeduint32 // 队列里面元素类型 elemtype *_type // element type // 队列send索引 sendxuint// send index // 队列索引 recvxuint// receive index // 等待channel的G队列 。recvqwaitq// list of recv waiters // 向channel发送数据的G队列 。sendqwaitq// list of send waiters // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G's status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. // 全局锁 lock mutex}复制代码
通过该hchan的数据结构和makechan函数,数据结构里有几个值得说明的数据:- dataqsiz表示channel的长度,如果为非缓冲队列,则值为0 。通过dataqsiz实现环形队列 。
推荐阅读
- 一文搞懂GoLang定时器实现原理
- 彻底搞懂虚拟地址翻译为物理地址的过程
- 一文看懂USB4
- 彻底搞懂虚拟内存,虚拟地址,虚拟地址空间
- 如何彻底搞懂Mysql事务原理
- MySQL高级进阶:关于InnoDB存储结构,一文深入分析讲解
- 帮你彻底搞懂 JS 中的 prototype、__proto__与constructor
- Java NIO的三大核心配件:通道、缓冲区、选择器
- 一文看懂技术中台、研发中台、移动中台建设全攻略
- 一文弄清Python网络爬虫解析库!内含多个实例讲解