Golang中为什么这里会出现死锁?
Golang中为什么这里会出现死锁? 我正在学习Go语言中的死锁及其产生条件。我正在查看这段代码。我知道这里可能会出现死锁,但我找不到它。有人能给我解释一下吗?
package main
import "fmt"
func reuters(ch chan string) {
ch <- "REUTERS"
}
func bloomberg(ch chan string) {
ch <- "BLOOMBERG"
}
func newsReader(reutersCh chan string, bloombergCh chan string) {
ch := make(chan string, 5)
go func() {
ch <- (<-reutersCh)
}()
go func() {
ch <- (<-bloombergCh)
}()
x := <-ch
fmt.Printf("got news from %s \n", x)
}
func main() {
reutersCh := make(chan string)
bloombergCh := make(chan string)
go reuters(reutersCh)
go bloomberg(bloombergCh)
newsReader(reutersCh, bloombergCh)
newsReader(reutersCh, bloombergCh)
}
更多关于Golang中为什么这里会出现死锁?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
对 newsReader 的第一次调用会读取两个值,因此在第二次调用时就没有内容可读了。
更多关于Golang中为什么这里会出现死锁?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这段代码确实存在死锁问题。让我分析一下原因:
死锁分析
问题出现在 newsReader 函数中。当第二次调用 newsReader 时,两个 reutersCh 和 bloombergCh 通道都是无缓冲的,并且已经没有任何goroutine向它们发送数据了。
func newsReader(reutersCh chan string, bloombergCh chan string) {
ch := make(chan string, 5)
go func() {
ch <- (<-reutersCh) // 这里会阻塞等待reutersCh有数据
}()
go func() {
ch <- (<-bloombergCh) // 这里会阻塞等待bloombergCh有数据
}()
x := <-ch
fmt.Printf("got news from %s \n", x)
}
具体执行流程:
-
第一次调用
newsReader时:- 两个goroutine分别从
reutersCh和bloombergCh接收数据 - 主函数中的
go reuters(reutersCh)和go bloomberg(bloombergCh)会发送数据 - 其中一个goroutine成功接收并发送到
ch,函数正常结束
- 两个goroutine分别从
-
第二次调用
newsReader时:- 再次创建两个goroutine等待从
reutersCh和bloombergCh接收数据 - 但是
reuters和bloomberggoroutine已经执行完毕,不会再发送数据 - 两个goroutine都会永久阻塞等待数据
- 主goroutine在
x := <-ch处也会永久阻塞等待 - 形成死锁
- 再次创建两个goroutine等待从
示例修复代码:
package main
import "fmt"
func reuters(ch chan string) {
ch <- "REUTERS"
}
func bloomberg(ch chan string) {
ch <- "BLOOMBERG"
}
func newsReader(reutersCh chan string, bloombergCh chan string) {
ch := make(chan string, 5)
go func() {
ch <- (<-reutersCh)
}()
go func() {
ch <- (<-bloombergCh)
}()
x := <-ch
fmt.Printf("got news from %s \n", x)
}
func main() {
reutersCh := make(chan string)
bloombergCh := make(chan string)
// 为每个newsReader调用创建新的goroutine发送数据
go func() {
go reuters(reutersCh)
go bloomberg(bloombergCh)
newsReader(reutersCh, bloombergCh)
}()
go func() {
go reuters(reutersCh)
go bloomberg(bloombergCh)
newsReader(reutersCh, bloombergCh)
}()
// 等待goroutine完成
select{}
}
或者使用带缓冲的通道:
func main() {
// 使用缓冲通道避免死锁
reutersCh := make(chan string, 2)
bloombergCh := make(chan string, 2)
go reuters(reutersCh)
go bloomberg(bloombergCh)
go reuters(reutersCh)
go bloomberg(bloombergCh)
newsReader(reutersCh, bloombergCh)
newsReader(reutersCh, bloombergCh)
}
死锁的根本原因是:无缓冲通道需要发送和接收操作同时准备好才能进行,而第二次调用时只有接收方在等待,没有发送方。

