得到一个致命错误:所有的 goroutine 都睡着了——死锁!有一个简单的测试场景

我正在尝试重现一个问题,并使用以下代码找到了最小用例。如果我关闭所有通道(绕过 i == 0 测试),一切都会按预期进行。Wg 状态递减并触发完成,主要退出正常。当我跳过关闭这些通道之一(故意)时,我希望主例程等待,而在这种情况下等待组信号量将无限期阻塞。相反,我收到了一个错误:“致命错误:所有 goroutine 都睡着了 - 死锁!”。这是为什么?我一定错过了一些基本的东西,还是运行时过于热心了?


package main


import (

    "fmt"

    "sync"

)


const N int = 4


func main() {


    done := make(chan struct{})

    defer close(done)


    fmt.Println("Beginning...")


    chans := make([]chan int, N)

    var wg sync.WaitGroup


    for i := 0; i < N; i++ {

        wg.Add(1)

        chans[i] = make(chan int)

        go func(i int) { // p0

            defer wg.Done()

            for m := range chans[i] {

                fmt.Println("Received ", m)

            }

            fmt.Println("Ending p", i)

        }(i)

    }


    go func() {

        wg.Wait()

        done <- struct{}{} // signal main that we are done

    }()


    for i := 0; i < N; i++ {

        fmt.Println("Closing c", i)

        if i != 0 { // Skip #0 so wg doesn't reach '0'

            close(chans[i])

        }

    }


    <-done // wait to receive signal from anonymous join function

    fmt.Println("Ending.")

}

更新:我编辑了代码以避免竞争条件。仍然收到此错误。


if i != 0之所以存在,是因为它是故意的。我希望 wg.Wait 永远阻塞(其信号量永远不会达到 0。)为什么我不能这样做?这似乎与我在其他地方<-done没有匹配的情况下使用done <- struct{}{}一样。在这种情况下编译器也会抱怨吗?


慕村9548890
浏览 179回答 2
2回答

小怪兽爱吃肉

这是发生了什么:第一个go func(i int) {goroutine 没有退出,因为chans[0]没有关闭。因为 goroutine 没有退出,wg.Done也没有被调用。wg.Wait()由于上一点,对永远阻塞的调用。Main 永远阻塞,因为信号没有发送到done。您可以通过删除 来解决死锁if i != 0 {,但还有另一个问题。等待组有一场比赛。有可能在调用 wg.Add(1) 之前调用 wg.Done()。在启动 goroutine 之前调用 wg.Add() 以避免竞争。

守着一只汪

if你的 for 循环中的语句不会让最后一个通道关闭,所以你goroutine要等待发生的事情,chans[i]这将阻止defer wg.Done()永远发生的事情,这反过来永远不会让wg.Wait()完成,THENNNNN 永远不会让done <- struct{}{}得到信号简而言之,您if statement的循环并没有关闭最后一个通道并导致死锁,因为没有人无能为力。正如@CodingPickle 确实指出的那样,将您移动wg.Add(1)到您的开头for loop以防止任何竞争条件http://play.golang.org/p/j1D5LZGUhd
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go