我不明白为什么这不起作用 https://play.golang.org/p/_ALPii0pXV6 但这 https://play.golang.org/p/vCOjAr-o54e 有效。
据我了解,goroutine异步发送到值true到a,12发送到b。在主函数中,a 被阻止,直到它收到一个值。为什么当我重新排列它以使 b 在 a 之前被阻塞时,会导致死锁?
Go 通道默认为无缓冲。这意味着在接收器读取通道之前,它无法在通道上发送。这实际上是 Go 首选模式。在大多数情况下,它比缓冲通道更有效。
对于您的第一个代码来说,这意味着 goroutine 在完成对通道 a 的写入之前无法继续写入通道 b。在主 goroutine 读取 a 之前,它无法做到这一点。
Go by Example 解释说,默认情况下,通道发送和接收会一直等到发送例程和接收例程都准备就绪。以下示例使这种阻塞变得明显:
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}
这段代码导致死锁,因为唯一的goroutine(主要的)卡在ch <- 1
,等待另一个goroutine接收。它几乎不知道我们期待它成为下一行的接收器。
这就解释了为什么你的第一个示例不起作用,因为另一个 goroutine 不会在b
上发送,直到它在 a
上的发送操作完成。但是主例程在b
收到之前不会在a
上收到!所以两者都被困在永远等待。
若要阅读有关此类操作(称为同步操作)的详细信息,请查看此说明。
执行的方式重写代码,那么就会更清楚发生了什么。
原始代码:
func main() {
a := make(chan bool)
b := make(chan int64)
go func(a chan bool, b chan int64) {
fmt.Println("Here1")
a <- true
b <- 12
} (a,b)
fmt.Println("Here2")
fmt.Println(fmt.Sprintf("%d", <-b))
fmt.Println(fmt.Sprintf("%v", <-a))
}
相同代码顺序执行的紧密表示:
a := make(chan bool)
b := make(chan int64)
fmt.Println("Here2") // Prints
// Pass control to new goroutine
fmt.Println("Here1")
a <- true // Write to channel a and block goroutine here and pass control to main
fmt.Println(fmt.Sprintf("%d", <-b)) // Tries to read from b but nothing has been written to it so blocks. At this point all your goroutines are blocked hence the deadlock.
fmt.Println(fmt.Sprintf("%v", <-a)) // doesn't even reach here.
b <- 12
}