select有3个特色分别是:
- nil的通道永远阻塞
- 如何跳出for -select 循环
- select{} 永远阻塞
nil通道永远阻塞
当case上读取一个通道时,如果这个通道是 nil , 则该case 永远阻塞,这个特性我们可以自定义将某个信道强制进行阻塞
func CombineChannel(ch1,ch2 chan int) <-chan int {
out := make(chan int,3)
go func() {
defer close(out)
for {
select {
case v1 , ok := <-ch1:
if !ok {
ch1 = nil
continue
}
out <- v1
case v2 , ok := <-ch2:
if !ok {
ch2 = nil
continue
}
out <- v2
}
if ch1 == nil && ch2 == nil {
break
}
}
}()
return out
}
复制代码
发送者可以通过close 关闭一个信道,接受者可以按如下方式来接收:
//如果信道被关闭或者指向了nil , 这 OK 就是 false
v , ok = <-c
复制代码
跳出for-select 循环
在select 中的break 并不能跳出for-select 循环 。 既然break 不能跳出for-select 循环,我们可以另辟蹊径去跳出,总共有3招
- 在满足条件的case内 , 使用return 结束协程
- 在select 外for内使用break跳出
- 使用goto , goto 只要你不乱定义,适当使用还是不错的
package selectapply
import (
"fmt"
"time"
)
func addX(c chan int,s chan string) {
for i:=0;i<10;i++ {
time.Sleep(time.Second)
c <-i
}
s <- "a"
}
func addY(c chan int,s chan string) {
for i:=0;i<20;i++ {
time.Sleep(time.Second)
c <-i
}
s <- "b"
}
func SelectBaisc() {
c1 := make(chan int)
c2 := make(chan int)
sigle := make(chan string,2)
go addX(c1 , sigle)
go addY(c2,sigle)
var nums []string
for {
select {
case t1 := <-c1:
fmt.Println("addX :", t1)
case t2 := <-c2:
fmt.Println("addY :", t2)
case t3 := <-sigle:
p("接收到了信号")
nums = append(nums,t3)
//下面这种写法,nums会进行重新赋值,而不会累加
//nums := append(nums,t3)
p("nums :",nums,"****length****",len(nums))
if len(nums) == 2{
goto TAG
}
}
}
TAG: return
}
复制代码
select{} 永远阻塞
select{} === ch := make(chan int)
复制代码
永远阻塞的作用在哪里呢? 当我们开发一个程序的时候,main函数千万不能在子协程干完活前退出,不然所有的子协程就都被迫退出了。服务也就被迫停止了。