目录
select定义:
select
语句用于在多个发送/接收信道操作中进行选择。select
语句会一直阻塞,直到发送/接收操作准备就绪。如果有多个信道操作准备完毕,select
会随机地选取其中之一执行。该语法与switch
类似,所不同的是,这里的每个case
语句都是信道操作。注:select 最好是带有default,这样避免信道没有返回响应(一直没有信息输出)而导致堵塞,会造成死锁。
一.select应用
select的实际应用含义如下: 假设我们有一个关键性应用,需要尽快地把输出返回给用户。这个应用的数据库复制并且存储在世界各地的服务器上。假设函数 server1 和 server2 与这样不同区域的两台服务器进行通信。每台服务器的负载和网络时延决定了它的响应时间。我们向两台服务器发送请求,并使用 select 语句等待相应的信道发出响应。select 会选择首先响应的服务器,而忽略其它的响应。使用这种方法,我们可以向多个服务器发送请求,并给用户返回最快的响应了
//demo1
ch1 := make(chan string)
ch2 := make(chan string)
go server1(ch1)
go server2(ch2)
for {
time.Sleep(time.Millisecond * 1000)
select {
case s1 := <-ch1:
fmt.Println(s1)
case s2 := <-ch2:
fmt.Println(s2)
default:
fmt.Println("nothing server receive")
}
}
1.主程序到select-case语句时,这里会发生阻塞。直到其中有case准备就绪,才会进行输出最先准备好的信道信息 2.在没有 case 准备就绪时,可以执行 select 语句中的默认情况(Default Case)。这通常用于防止 select 语句一直阻塞。
二.select_channel遇到的死锁问题
//1
ch1 := make(chan string)
select {
case s1 := <-ch1:
fmt.Println(s1)
default:
fmt.Println("nothing server receive")
}
1. 这里的第一个demo,如果没有加 default(默认情况),而上面case 试图接受信道中的信息,但却没有人进行向信道发送信息,就会出现死锁问题。
2.如果select含有值为nil的信道,同样会执行默认情况default。这里的ch1就等于nil,如果没有默认情况default,会一直阻塞,导致死锁
//2
select {}
这里若是没有case执行,则select语句会一直阻塞着,导致死锁
三.select的随机选取
ch3 := make(chan string)
ch4 := make(chan string)
go server3(ch3)
go server4(ch4)
time.Sleep(1 * time.Second)
select {
case s1 := <-ch3:
fmt.Println(s1)
case s2 := <-ch4:
fmt.Println(s2)
当select中两个case都准备就绪时,会进行随机输出。若在playground 上在线运行的话,它的输出总是一样的,这是由于 playground 不具有随机性所造成的。
四.如何判断通道是否关闭
//用select_channel结合就可以解决
ch := make(chan int, 10)
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
}()
time.Sleep(3 * time.Second)
close(ch)
for i := 0; i < 10; i++ { //这里不断循环,进行遍历通道里的内容
select {
case msg, ok := <-ch:
if ok {
fmt.Println(msg)
} else {
fmt.Println("channel is closing")
}
default:
fmt.Println("正在等待通道输出信息")
}
}
这里可以通过select_channel结合进行解决,通过close(channel) 内置函数,返回的形式 x,ok:=<-ch,其中通过 ok若为true则通道还未关闭,还在发送数据。若为fasle,则通道已经关闭,无数据发送。
这里最好是带有default,避免通道堵塞,造成死锁。
五.对已关闭的通道进行写值
c := make(chan int, 2)
close(c)
c <- 1
会出现panic: send on closed channel
六.对已关闭的通道进行读值
c := make(chan int, 2)
c <- 1
c <- 2
close(c)
value, ok := <-c
fmt.Println(value, ok)
value, ok = <-c
fmt.Println(value, ok)
value, ok = <-c
fmt.Println(value, ok)
1.读已经关闭的 chan 能一直读到东西,但是读到的内容根据通道内关闭前是否有元素而不同。 2.如果 chan 关闭前,buffer 内有元素还未读 , 会正确读到 chan 内的值,且返回的第二个 bool 值(是否读成功)为 true。 3.如果 chan 关闭前,buffer 内有元素已经被读完,chan 内无值,接下来所有接收的值都会非阻塞直接返回,返回 channel 元素的零值(如果是string就返回空字符串),但是第二个 bool 值一直为 false。