Channel管道的基础(一)
func main(){
ch := make(chan interface{}) // 无缓冲管道
对于没有缓存的通道,需要接收方实时接收数据,否则会报错
ch1 := make(chan interface{}, 10) // 带缓冲区的管道
// 向管道发送数据
ch1 <- struct{}
ch1 <- 0
ch1 <- "hello"
select{ // 专门用来选择管道的功能
case cd <- struct{}{}:
default :
}
}
阻塞接收数据
date := <-ch1
非阻塞接收数据
data, ok := <-ch1
data:表示接收的数据信息
ok:表示是否接收到该数据接收任何数据<-ch1
原理解析
func main(){
ch := make(chan interface{},1)
ch <- 0
}
go tool compile -N -l -S main.go
初始化方法实际上调用的就是 runtime.makechan方法,返回一个指针,来操作管道结构里面的值来操作数据
func 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 *hchan
switch {
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.ptrdata == 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 = elem
c.dataqsiz = uint(size)
lockInit(&c.lock, lockRankHchan)
if debugChan {
print("makechan: chan=", c, "; elemsize=", elem.size, "; dataqsiz=", size, "\n")
}
return c
}
1、如果创建一个无缓冲channel ,那么只需要为runtime.hchan本身分配一段内存空间即可。
2、如果创建的缓冲channel存储的类型不是指针类型,会为当前channel和存储类型元素的缓冲区,分配一块连续的内存空间。
3、在默认情况下(缓冲channel存储类型包含指针),会单独为runtime.hchan和缓冲区分配内存。
type hchan struct {
qcount uint // total data in the queue 队列中元素总数量
dataqsiz uint // size of the circular queue 队列容量
buf unsafe.Pointer // points to an array of dataqsiz elements 元素数据内存buffer区域
elemsize uint16 // 队列中每个对象的大小
closed uint32 // channel是否关闭
elemtype *_type // element type 队列中元素的类型
sendx uint // send index 发送位置索引
recvx uint // receive index 接收位置索引
recvq waitq // list of recv waiters 等待读消息的g队列
sendq waitq // list of send waiters 等待发消息的g队列
// 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 // 互斥锁
}
本质:创建一个channel就是得到一个runtime.hchan的指针,后续对此chan的操作,无非就是对该结构体中的相应字段进行操作。
保持并发的原因:channel内部使用了互斥锁来保证了并发安全若有收获