What you are wasting today is tomorrow for those who died yesterday; what you hate now is the future you can not go back.
你所浪费的今天是昨天死去的人奢望的明天; 你所厌恶的现在是未来的你回不去的曾经。
select介绍
select语句用于从多个发送/接收通道操作中进行选择。直到发送/接收操作之一准备就绪,select停止阻塞。如果多个操作准备就绪,随机选择其中一个。语法与switch类似,只是每个case语句都是一个通道操作。
让我们直接进入一些代码,以便更好地理解。
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
time.Sleep(2 * time.Second)
ch <- "from server1"
}
func server2(ch chan string) {
time.Sleep(1 * time.Second)
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
// time.Sleep(4*time.Second)
}
解释: main程启动两个goroutine来向通道中发送数据,select负责监听(一直处于阻塞状态),等待其中 一个goroutine发送数据成功,select 立即停止阻塞并接收数据后退出select。
试想:如果我们将代码中的注释部分开启,程序的输出内容有没有变化?
防止阻塞或default
当case没有准备好(获取到数据)时, default语句会执行,这样就防止了select的阻塞状态出现。
package main
import (
"fmt"
"time"
)
func process(in chan string) {
for {
time.Sleep(4 * time.Second)
in <- "数据写入"
}
}
func main(){
in := make(chan string)
go process(in)
for{
time.Sleep(2*time.Second)
select{
case str := <-in :
fmt.Println("接受到数据:",str)
default:
fmt.Println("等待中...")
}
}
}
当mian程启动一个goroutine向通道中写数据后, main程开始for监听in 通道, sleep 2秒后, <-in 未能读取到数据,select 打印default内的数据, 等到再次sleep后in通道传递数据。
死锁
当没有数据发送时,仅仅从通道中来接收数据,程序将会永久的阻塞, 因此会导致死锁。
func main() {
ch := make(chan string)
select {
case <-ch:
}
}
select从nil channel中接收数据
package main
import "fmt"
func main() {
var ch chan string
select {
case v := <-ch:
fmt.Println("received value", v)
default:
fmt.Println("default case executed")
}
}
总结: select 中default语句是默认执行, 防止程序的阻塞产生。
Random Select
如果case语句中有多个通道发送来数据, 那select将会如何输出?
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
ch <- "from server1"
}
func server2(ch chan string) {
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
time.Sleep(2 * time.Second) // 使所有goroutine的channel写入数据
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
}
select将会随机的从中选择输出, 因此输出结果可能不相同。
空select
我们上面说到, select直到case(在没有default的前提下)中接收到数据时,才会停止阻塞。
那么空select会发生什么?
package main
func main() {
select {}
}
程序永久性阻塞(引发死锁)。
有疑问加站长微信联系(非本文作者)