Go 语言最常提及的设计模式就是:不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存。许多主流编程语言中,多个线程传递数据的方式是共享内存,其中会涉及到线程之间竞争锁的问题。在Go中也提供了共享内存加互斥锁进行通信的方式,但Go还提供了一种不同的并发模型,即通信顺序进程(Communicating sequential processes,CSP),其中goroutine和channel分别对应CSP模型中的实体和传递数据的媒介。在Go中,goroutine可通过channel进行通信。
一、数据结构
type hchan struct {
qcount uint // 缓冲区中数据的数量
dataqsiz uint // 缓冲区的大小
buf unsafe.Pointer // 指向缓冲区的指针
elemsize uint16 // 元素的大小
closed uint32 // channel是否关闭
elemtype *_type // e元素类型
sendx uint // 发送操作处理到的位置
recvx uint // 接收操作处理到的位置
recvq waitq // 接收队列
sendq waitq // 发送队列
lock mutex // 互斥锁
}
二、操作
1.创建channel
创建channel最终是通过makechan方法操作的,输入为要创建的channel的类型和大小,并返回一个hchan的指针
func makechan(t *chantype, size int) *hchan
首先对要创建的channel进行检查,如元素大小是否合法、要创建的channel大小是否合法等
elem := t.elem
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"))
}
接着为channel分配内存并初始化hchan的其他参数
var c *hchan
switch {
case mem == 0:
c = (*hchan)(mallocgc(hchanSize, nil, true))
c.buf = c.raceaddr()
case elem.ptrdata == 0:
c = (*hchan)(mallocgc(hchanSize+mem, nil, true))
c.buf = add(unsafe.Pointer(c), hchanSize)
default:
c = new(hchan)
c.buf = mallocgc(mem, elem, true)
}
c.elemsize = uint16(elem.size)
c.elemtype = elem
c.dataqsiz = uint(size)
然后channel创建成功并返回
2.发送数据
当通过c <- x 发送数据时,最后会调用到chansend方法,该方法接受一个channel的指针