本文为翻译文章
死锁和 default
package main
func main() {
ch := make(chan string)
select {
case <-ch:
}
}
在上面的程序中,我们创建了一个 ch 通道。我们尝试从 select 语句里面读取 ch 通道。select 语句将永远阻塞,因为没有其他 Goroutine 正在写入此通道,因此将导致死锁。该程序将在运行时发生 panic
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/tmp/sandbox627739431/prog.go:6 +0x4d
如果存在 default,则不会发生此死锁,因为将在没有其他情况准备好时执行 default。
上面的程序用下面的 default 重写:
package main
import "fmt"
func main() {
ch := make(chan string)
select {
case <-ch:
default:
fmt.Println("default case executed")
}
}
程序输出:
default case executed
同样,即使选择只 nil 通道,也会执行default。
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")
}
}
在上面的程序中,ch 通道的值为 nil,我们尝试从 select 中读取 ch 通道里的数据。如果 default 不存在,则该 select 将永远受阻并造成死锁。由于我们在选择框内有一个 default,它将被执行并且程序将打印出来:
default case executed
随机选择
当 select 语句中的多个 case 准备就绪时,将随机执行其中之一。
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(1 * time.Second)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
}
在上面的程序中,调用了 server1 和 server Goroutine。然后,主程序休眠 1 秒钟。当到达 select 语句时。server1 已经写入字符串 from server1 到 output1 通道,server2 已经写入字符串 from server2 到 output2通道,因此这两种情况下都准备好执行。如果您多次运行此程序,则输出将在随机选择的情况之间 from server1 或 from server2 取决于选择的情况而有所不同。
陷阱 — 空 select
package main
func main() {
select {}
}
您认为上面程序的输出是什么?
我们知道 select 语句将阻塞直到执行其中一种情况。在这种情况下,select 语句没有任何情况,因此它将永远阻塞,从而导致死锁。该程序将发生 panic:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
main.main()
/tmp/sandbox246983342/prog.go:4 +0x25