go Channel源码分析

理论基础

不要通过共享内存来通信,要通过通信来共享内存。

特点

通道分为非缓冲通道和缓冲通道。
单向通道由双向通道转换而来,但是单向通道不能转换回双向通道。 
通过make来初始化一个chan,未初始化的chan的零值是nil。 
接收数据时,返回两个值。第一个值是返回的chan中的元素,第二个值是bool类型,代表是否成功从chan中读取到一个值。如果是false,那么chan已经被close而且chan中没有元素,第一个返回值是零值。
不要在双向通道的接收端关闭通道,由写入者关闭通道,避免向已经关闭的通道写数据导致的panic。

panic 
(1)向已经关闭的通道写数据
(2)重复关闭通道
(3)close nil的channel
阻塞 
(1)向未初始化(nil channel)的通道写数据或读数据
(2)向缓冲区已满的通道写入数据
(3)通道中没有数据,读取该通道

使用

定义

var c chan int // c == nil,c is channel of int 
c := make(chan int) 
 
c := make(chan<- int) // 定义只能发送数据的通道 
c := make(<-chan int) // 定义只能接收数据的通道 

非缓冲通道的发送与接收默认是阻塞的
当数据写入通道时,在发送数据的语句处阻塞,直到其他协程从通道读取数据。 
当从通道读取数据时,在读取数据的语句处阻塞,直到其他协程把数据写入通道。

package main

import (
	"fmt"
)

func hello(done chan bool) {
	fmt.Println("Hello world goroutine")
	done <- true
}

func main() {
	done := make(chan bool)
	go hello(done)
	<-done
	fmt.Println("main function")
}

运行结果

死锁(所有协程都阻塞)

package main

func main() {
	ch := make(chan int)
	ch <- 5
	<-ch
}

非缓冲通道造成goroutine泄漏

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan bool)

	go func() {
		time.Sleep(2 * time.Second)
		ch <- true // block
		fmt.Println("end")
	}()

	select {
	case <-ch:
		fmt.Println("get data")
	case <-time.After(time.Second):
		fmt.Println("timeout")
	}
}

运行结果

通道中持续接收元素(直到通道关闭且通道里面没有元素)

for c := range ch { 
    fmt.Println(c) 
}

实现原理

go/src/runtime/chan.go
chan是多生产者多消费者的模式,通过循环队列+mutex来存取元素。如果消费者因为没有数据可取而被阻塞了,就会被加入到recvq队列中。如果生产者因为循环队列满了而阻塞,会被加入到sendq队列中。

send

如果chan是nil,那么永久阻塞调用者。
如果chan已经被close,那么会panic。
针对非缓冲通道场景,如果recvq中有receiver,那么唤醒一个receiver,把数据交给它。
如果循环队列未满,那么把数据放入到循环队列中。
如果循环队列满了,那么把sender加入sendq,sender阻塞等待,直到循环队列未满或者chan被close。

recv

如果chan是nil,那么永久阻塞调用者。
如果chan已经被close,并且循环队列中没有元素,那么返回零值和false。
针对非缓冲通道场景,如果sendq中有sender,那么唤醒一个sender,把sender的数据交给receiver。
如果循环队列满了,那么取出首元素给receiver。这时,如果sendq中有sender,那么唤醒一个sender并把sender的数据放入循环队列。
如果循环队列中有元素,那么取出首元素给receiver。
如果循环队列中没有元素,那么把receiver加入recvq,receiver阻塞等待,直到循环队列有元素或者chan被close。

close

如果chan是nil或者已经被close,那么会panic。
唤醒sendq中的sender和recvq中的receiver。

posted on 2023-01-21 09:59  王景迁  阅读(50)  评论(0编辑  收藏  举报

导航