目录
通道类型的值本身就是并发安全的,这也是 Go 语言自带的、唯一一个可以满足并发安全性的类型。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
通道声明及初始化
通道相当于一个先进先出的队列。在声明一个通道类型变量的时候,我们首先要确定该通道类型的元素类型,决定了我们可以通过这个通道传递什么样的数据。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
声明并初始化一个通道时需要使用内建函数 make
。 make
第二个参数可选,用于表示通道的容量。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
ch := make(chan int, 2)
当容量为0时,该通道为 非缓冲通道
。大于0时为 缓冲通道
。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
元素值的发送和接受都需要使用操作符 <-
(也叫接送操作符)。用法如下:文章源自编程技术分享-https://mervyn.life/ae7138ec.html
-
ch <-
将元素发送至通道 ch文章源自编程技术分享-https://mervyn.life/ae7138ec.html -
<- ch
也被叫做接收表达式,用于表达从该通道接收一个元素值。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
package main
import "fmt"
func main() {
ch := make(chan int, 3)
ch <- 4 //像通道ch 发送一个元素值2
ch <- 3
ch <- 1
elem := <- ch //从通道中接收一个元素值
fmt.Println(elem) //4
fmt.Println(len(ch)) //2
}
引发阻塞的操作
缓冲通道
如果通道已满,那么所有的发送到该通道的操作都会被阻塞,直到通道中有元素被接收走。通道会优先通知最早因此等待的那个操作所在的goroutine,再次进行发送。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
如果通道已空,那么对它的所有接收操作都会被阻塞,直到通道中有新元素出现。道会优先通知最早因此等待的那个操作所在的goroutine,再次进行接收。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
非缓冲通道
无论是发送还是接收,一开始执行就会被阻塞,直到出现配对的操作也开始,才会继续传递。由此可见,非缓冲通道是采用同步的方式进行传递。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
值为nil的通道
对值为nil的通道进行发送和接收都会永久阻塞状态。它们所属的goroutine中的代码都不再会执行。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
由于通道类型是引用类型,所以它的零值就是 nil 。所以只声明了该类型的变量但是没有用make 初始化时,它的值就会是 nil文章源自编程技术分享-https://mervyn.life/ae7138ec.html
引发panic的操作
下边罗列一下会引发 panic
的情况:文章源自编程技术分享-https://mervyn.life/ae7138ec.html
- 通道关闭后,继续向它进行发送操作
- 试图关闭一个已经关闭的通道
千万不要让接收方关闭通道,应该让发送方做这件事文章源自编程技术分享-https://mervyn.life/ae7138ec.html
单向通道
只能发不能收或者只能收不能发的通道就是单向通道。例:文章源自编程技术分享-https://mervyn.life/ae7138ec.html
ch := make(chan<- int, 1)
上述通道表示只能发不能收。简称为 发送通道
文章源自编程技术分享-https://mervyn.life/ae7138ec.html
通道常用操作
package main
import "fmt"
func main() {
ch := getChan()
for elem := range ch {
fmt.Println(elem)
}
}
func getChan() <-chan int {
ch := make(chan int, 5)
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
return ch
}
下边例子为 select
与通道联合使用文章源自编程技术分享-https://mervyn.life/ae7138ec.html
package main
import (
"fmt"
"math/rand"
)
func main() {
channels := [3]chan int {
make(chan int, 1),
make(chan int, 1),
make(chan int, 1),
}
randInt := rand.Intn(3)
fmt.Println("value:", randInt)
channels[randInt] <- randInt
select {
case elem := <-channels[0]:
fmt.Println("first channel, value:", elem)
case elem := <-channels[1]:
fmt.Println("second channel, value:", elem)
case elem := <-channels[2]:
fmt.Println("third channel, value:", elem)
default:
fmt.Println("error")
}
}
select 有默认分支时不会被阻塞。文章源自编程技术分享-https://mervyn.life/ae7138ec.html
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 1)
time.AfterFunc(time.Second, func() {
close(ch)
})
select {
case _, ok := <-ch: //判断通道是否已关闭
if !ok {
fmt.Println("ch is closed")
break
}
}
}
文章源自编程技术分享-https://mervyn.life/ae7138ec.html
评论