Golang Channel

Channel解析

1. Channel源码分析
1.1 Channel数据结构
type hchan struct {
   
	qcount   uint           // channel的元素数量
	dataqsiz uint           // channel循环队列长度
	buf      unsafe.Pointer // 指向循环队列的指针
	elemsize uint16         // 元素大小
	closed   uint32         // channel是否关闭 0-未关闭
	elemtype *_type // 元素类型
	sendx    uint   // 当前已发送的元素在队列当中的索引位置
	recvx    uint   // 当前已接受的元素在队列当中的索引位置
	recvq    waitq  // 阻塞的接受goroutine
	sendq    waitq  // 阻塞的发送goroutine

	// 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
}

// waitq 是一个双向链表,里面保存了 goroutine
type waitq struct {
   
	first *sudog
	last  *sudog
}

channel底层示例图:

1.2 创建channel

Go中通过调用makechan(t *chantype, size int)来创建channel,源码分为如下两部分:

校验

func makechan(t *chantype, size int) *hchan {
   
	elem := t.elem

	// compiler checks this but be safe.
  // 元素大小不允许超过16kb
	if elem.size >= 1<<16 {
   
		throw("makechan: invalid channel element type")
	}
  // 判断当前的 hchanSize 是否是 maxAlign 整数倍,并且元素的对齐大小不能大于最大对齐的大小
	if hchanSize%maxAlign != 0 || elem.align > maxAlign {
   
		throw("makechan: bad alignment")
	}

	...
}

在校验时,有两个参数hchansizemaxAlign,值如下:

const (
   // 获取maxAlign 是内存对齐的最大值,这个等于 64 位 CPU 下的 cacheline 的大小
   maxAlign  = 8
   // hchanSize 计算 unsafe.Sizeof(hchan{}) 最近的 8 的倍数,一脸懵逼
   hchanSize = unsafe.Sizeof(hchan{
   }) + uintptr(-int(unsafe.Sizeof(hchan{
   }))&(maxAlign-1))
)

对于hchanSize举一个简单的例子,假设
n = u n s a f e . S i z e o f ( h c h a n ) = 14 n = unsafe.Sizeof(hchan{}) = 14 n=unsafe.Sizeof(hchan)=14
n的补码为00001110,-n的补码为11110010,则

-n & (maxAlign - 1) = 2

-n           11110010
maxAlign - 1 00000111
res          00000010

hchanSize = 16。

获取最近的8的倍数计算公式如下:
c = n + ( a − n % a ) c = n + (a - n\%a) c=n+(an%a)
这个公式就等同于
c = n + ( ( −

你可能感兴趣的:(golang)