channel 是golang中的一种数据结构,主要用来实现golang协程之间的通信,channel的设计也是提现了golang对于进程间消息传递的观点:“用通信实现共享内存,而不是使用共享内存实现通信”。
2. channel 数据结构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
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
lock mutex
}
2.1. 各个字段的含义以及作用
bufsendxrecvxqcountdataqsizeelemsizeclosedelemtypesendxrecvxrecvqsendqlock
elemsizeelemsize
recvq与sendq的结构:waitq
type waitq struct {
first *sudog
last *sudog
}
type sudog struct {
// Current goroutine
g *g
// isSelect indicates g is participating in a select, so
// g.selectDone must be CAS'd to win the wake-up race.
isSelect bool
next *sudog
prev *sudog
elem unsafe.Pointer // data element (may point to stack)
// The following fields are never accessed concurrently.
// For channels, waitlink is only accessed by g.
// For semaphores, all fields (including the ones above)
// are only accessed when holding a semaRoot lock.
acquiretime int64
releasetime int64
ticket uint32
parent *sudog // semaRoot binary tree
waitlink *sudog // g.waiting list or semaRoot
waittail *sudog // semaRoot
c *hchan // channel
}
不是很懂这些字段和作用…
2.2. channel工作过程:读与写
- 写
- 读
2.3. 操作 channel 后:成功、阻塞或者panic
truefalsetrue
2.4. range 与 select
rangerange
c := make(chan int, 10)
go func(){
for i:=0;i<5;i++{
c<-i
}
close(c)
}
for v,ok := range c {
if !ok{
fmt.Println(v,ok)
break
}
fmt.Println(v,ok)
}
// 输出
0 true
1 true
2 true
3 true
4 true
0 false
select
contextContextDone()DoneWithCancel()ContextCancalFunccancelDoneDoneDoneDone
func main() {
gen := func(ctx context.Context) <-chan int {
dst := make(chan int) // 无缓冲通道
n := 1
go func() {
for {
select {
case <-ctx.Done():
return
case dst <- n:
n++
}
}
}()
return dst
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for v := range gen(ctx) {
fmt.Println(v)
if v == 5 {
break
}
}
}
// 输出
1
2
3
4
5
2.5. channel 缓冲:有与无
有缓冲
有缓冲通道可以让goroutine之间的信息传递更加高效,减少不必要的阻塞,通过 2.3 小节我们知道
- 读一个非空的有缓冲通道不会阻塞
- 写一个非满的有缓冲通道不会阻塞
c := make(chan int, 10) // 定义一个最多可以缓存10个元素的带缓冲通道
无缓冲
无缓冲通常用来同步两个goroutine,对无缓冲通道的所有读(写)操作都将阻塞直到有写(读)操作发生
c := make(chan int) // 定义一个不带缓冲的通道