摘要:今天我们来学习 Go 语言中 channel , 这是 Go 语言中非常重要的一个特性。
在使用 channel 之前需要使用 make 来创建一个 channel, 如果在使用之前没有使用 make, 则会造成死锁(原因在后面死锁部分进行说明)
ch := make(chan int) //创建无缓冲的channel ch := make(chan int, N) //创建有缓冲的channel
N := <- ch //读操作 ch <- N //写操作
无缓冲:发送和接收动作是同时发生的,如果没有 goroutine 读取(<- ch),则发送者(ch <- )会一致阻塞
有缓冲:channel 类似一个有容量的队列,当队列满的时候发送者会阻塞,当队列空的时候接收者会阻塞
channel 不像文件,通常不需要去关闭它们,只有在接收者必须要知道没有更多的数据的时候,才需要发送端去关闭(只有客户端才应该去关闭),比如在 range 循环遍历中。
ch := make(chan int) close(ch) //关闭 channel
关闭需要注意的几点:
1, 重复关闭 channel 会导致 panic 2, 向关闭的 channel 发送数据会导致 panic 3, 从关闭的 channel 读数据不会发生 panic, 读出 channel 中已有的数据之后, 读出的就是 channel 中值类型的默认值
判断一个 channel 是否关闭可以使用 ok-idiom 的方式,这种方式在 map 中比较常用:
//ok-idiom 方式 val, ok := <- ch if ok == false { fmt.println("closed") }else { fmt.println("not closed") }
func TestChannel() { ch := make(chan int) go func() { ch <- 1 }() fmt.Println(<- ch) }
select { case v, ok := <- ch1: if(!ok) { fmt.println("ch1 channel closed!") }else { fmt.println("ch1 do something") } case v, ok := <- ch2: if(!ok) { fmt.println("ch2 channel closed!") }else { fmt.println("ch2 do something") } default: fmt.println("ch1 not ready and ch2 not ready") }
range 可以直接取到 channel 中的值,当我们使用 range 来操作 channel 的时候, 一旦 channel 关闭,channel 内部数据读完之后循环自动结束
//消费者 func consumer(ch chan int) { for x := range ch { fmt.Println(x) // do something with x } } //生产者 func producer(ch chan int) { values := make([]int, 5) for _, v := range values { ch <- v } }
func queryDb(ch chan int) { time.Sleep(time.Second) ch <- 100 } func main() { ch := make(chan int) go queryDb(ch) t := time.NewTicker(time.Second) select { case v := <- ch: fmt.Println("res: ", v) case <- t.C: fmt.Println("timeout") }
func deadlock1() { ch := make(chan int) ch <- 1 } func deadlock2() { ch := make(chan int) <- ch }
死锁分析:无缓冲信道不存储值,无论是传值还是取值都会阻塞,无缓冲信道必须同时传值和取值。
func Deadlock3() { ch1 := make(chan string) ch2 := make(chan string) go func() { ch2 <- "ch2 value" // block point ch1 <- "ch1 value" }() <- ch1 //block point <- ch2 }
死锁分析: 在 main goroutine 中,ch1 等待数据,而在 goroutine 中,ch2 在等待数据,所以造成死锁。
func Deadlock4() { chs := make(chan string, 2) chs <- "first" chs <- "second" //close(chs) 需要发送端主动去关闭 for ch := range chs { fmt.Println(ch) } }
死锁分析: 从空 channel 中读取会导致阻塞,同死锁情况1。
参考资料:
https://tour.golang.org/concurrency/4
原文:https://www.cnblogs.com/zpcoding/p/13169028.html