channel特点
- chan类型的值本身的并发安全的,因此可用于多个goroutine之间通信
- chan使用make初始化,make第二个参数表示chan的容量。为0表示非缓冲通道,大于0表示缓冲通道
- chan相当于FIFO队列,每个元素都有严格的发送顺序和接受顺序。同一个chan中,发送和接受互斥
- 对于带缓冲的channel,如果通道已满,则发送方会被阻塞,直到元素被接受走;如果通道已空,则接收方阻塞。对于非缓冲通道,无论发送还是接受,一开始执行就被阻塞,直到配对的操作开始执行,是一种同步的方式,数据直接从发送方复制到接收方,不在通道中中转。带缓冲的异步的。
- 引发panic的几种: 关闭一个已经关闭了的通道;向已经关闭了的通道中发送数据
- 不能让接收方关闭通道
channel高级用法
- 单向通道,在字面量中体现;作用是约束其他代码的行为
var lesschan = make(chan<-int, 1)
紧挨在chan的右边的<-,表示只能发;左边只能收。
intChan1 := make(chan int, 3)
SendInt(intChan1)
func SendInt(ch chan<- int) {
ch <- rand.Intn(1000)
}
上面函数中需要单向通道的参数,传递的是双向通道值。编译器可将双向转为单向
channel的操作
range
使用带range
的子句的for
语句从通道中获取数据
intChan2 := getIntChan()
for elem := range intChan2 {
fmt.Printf("The element in intChan2: %v\n", elem)
}
func getIntChan() <-chan int {
num := 5
ch := make(chan int, num)
for i := 0; i < num; i++ {
ch <- i
}
close(ch)
return ch
}
The element in intChan2: 0
The element in intChan2: 1
The element in intChan2: 2
The element in intChan2: 3
The element in intChan2: 4
select
也可以使用select
操纵通道
intChan := make(chan int, 1)
// 一秒后关闭通道。
time.AfterFunc(time.Second, func() {
close(intChan)
})
select {
case _, ok := <-intChan:
if !ok {
fmt.Println("The candidate case is closed.")
break
}
fmt.Println("The candidate case is selected.")
}
关闭通道后,ok值为false
// 准备好几个通道。
intChannels := [3]chan int{
make(chan int, 1),
make(chan int, 1),
make(chan int, 1),
}
// 随机选择一个通道,并向它发送元素值。
index := rand.Intn(3)
fmt.Printf("The index: %d\n", index)
intChannels[index] <- index
// 哪一个通道中有可取的元素值,哪个对应的分支就会被执行。
select {
case <-intChannels[0]:
fmt.Println("The first candidate case is selected.")
case <-intChannels[1]:
fmt.Println("The second candidate case is selected.")
case elem := <-intChannels[2]:
fmt.Printf("The third candidate case is selected, the element is %d.\n", elem)
default:
fmt.Println("No candidate case is selected!")
}
select
中一次只能选择一个case
语句执行
chan思考
- select语句发现通道关闭,如何屏蔽它所在的分支
为防止再次进入关闭的分支,可以将channel重新赋值为非缓冲通道,这样就一直被阻塞了。
for{
select{
case _, ok : <- ch1:
if !ok{
ch1 = make(chan int)
}
}
}
- 如何退出select外层的for循环
在select中case语句后面的break仅仅是跳出select,并不能跳出外层的for循环。
loop:
for{
select {
case <-time.After(time.Second * 2):
fmt.Println("tick event...")
case <- exitChan:
fmt.Println("exit event...")
break loop
}
}
break和标签使用,直接跳出制定的循环体。
for{
select {
case <-time.After(time.Second * 2):
fmt.Println("tick event...")
case <- exitChan:
fmt.Println("exit event...")
goto loop
}
}
loop:
fmt.Println("end")
goto到制定标签
- 你不希望所有的通道阻塞,可以使用超时策略
var c <-chan int
select {
case <-c: //1
case <-time.After(1 * time.Second):
fmt.Println("Timed out.")
}
- 永久超时的写法
select {}
网友评论