一个select死锁问题
以下代码的输出结果:
func main() {
var wg sync.WaitGroup
foo := make(chan int)
bar := make(chan int)
wg.Add(1)
go func() {
defer wg.Done()
select {
case foo <- <-bar:
default:
println("default")
}
}()
wg.Wait()
}
结果
解析
-
对于 select 语句,在进入该语句时,会按源码的顺序对每一个 case 子句进行求值:这个求值只针对发送或接收操作的额外表达式。将代码改为:
func main() { var wg sync.WaitGroup foo := make(chan int) bar := make(chan int) wg.Add(1) go func() { defer wg.Done() select { case val:=<-bar: foo<-val // case foo <- <-bar: default: println("default") } }() wg.Wait() }
此时,正常输出:default
-
所以上述语句的理解就是:getVal()、<-input 和 getch() 等类似的操作会执行,导致死锁
package main import ( "fmt" ) func main() { ch := make(chan int) go func() { select { case ch <- getVal(1): fmt.Println("in first case") case ch <- getVal(2): fmt.Println("in second case") default: fmt.Println("default") } }() fmt.Println("The val:", <-ch) } func getVal(i int) int { fmt.Println("getVal, i=", i) return i }
无论 select 最终选择了哪个 case,getVal() 都会按照源码顺序执行:getVal(1) 和 getVal(2),必然输出:getVal, i= 1 | getVal, i= 2
相同问题
为什么每次都是输出一半数据,然后死锁?
package main
import (
"fmt"
"time"
)
func talk(msg string, sleep int) <-chan string {
ch := make(chan string)
go func() {
for i := 0; i < 5; i++ {
ch <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(sleep) * time.Millisecond)
}
}()
return ch
}
func fanIn(input1, input2 <-chan string) <-chan string {
ch := make(chan string)
go func() {
for {
select {
case ch <- <-input1:
case ch <- <-input2:
}
}
}()
return ch
}
func main() {
ch := fanIn(talk("A", 10), talk("B", 1000))
for i := 0; i < 10; i++ {
fmt.Printf("%q\n", <-ch)
}
}
- 每次进入以下 select 语句时:<-input1 和 <-input2 都会执行,相应的值是:A x 和 B x(其中 x 是 0-5)。但每次 select 只会选择其中一个 case 执行,所以 <-input1 和 <-input2 的结果,必然有一个被丢弃了,也就是不会被写入 ch 中。因此,一共只会输出 5 次,另外 5 次结果丢掉了。
- 而 main 中循环 10 次,只获得 5 次结果,所以输出 5 次后,报死锁。
更多类似的问题
// ch 是一个 chan int;
// getVal() 返回 int
// input 是 chan int
// getch() 返回 chan int
select {
case ch <- getVal():
case ch <- <-input:
case getch() <- 1:
case <- getch():
}
最典型的问题
有内存泄露(传递给 time.After 的时间参数越大,泄露会越厉害)
PS: 内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
func main() {
ch := make(chan int, 10)
go func() {
var i = 1
for {
i++
ch <- i
}
}()
for {
select {
case x := <- ch:
println(x)
case <- time.After(30 * time.Second):
println(time.Now().Unix())
}
}
}
- 每次执行select都会执行一次time.After(30*time.Second)获取一个chan。