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 时,两个 reutersChbloombergCh 通道都是无缓冲的,并且已经没有任何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)
}

具体执行流程:

  1. 第一次调用 newsReader 时:

    • 两个goroutine分别从 reutersChbloombergCh 接收数据
    • 主函数中的 go reuters(reutersCh)go bloomberg(bloombergCh) 会发送数据
    • 其中一个goroutine成功接收并发送到 ch,函数正常结束
  2. 第二次调用 newsReader 时:

    • 再次创建两个goroutine等待从 reutersChbloombergCh 接收数据
    • 但是 reutersbloomberg goroutine已经执行完毕,不会再发送数据
    • 两个goroutine都会永久阻塞等待数据
    • 主goroutine在 x := <-ch 处也会永久阻塞等待
    • 形成死锁

示例修复代码:

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)
}

死锁的根本原因是:无缓冲通道需要发送和接收操作同时准备好才能进行,而第二次调用时只有接收方在等待,没有发送方。

回到顶部