【问题标题】:fatal error: all goroutines are asleep - deadlock! , This error i am getting致命错误:所有 goroutine 都处于休眠状态 - 死锁! ,这个错误我得到
【发布时间】:2022-01-21 11:21:08
【问题描述】:

我在函数 FindPathConcurently 中收到此错误。由于我是 Go 新手,我正在尝试理解它,但无法提供任何帮助,我将不胜感激。我正在尝试解决我正在为迷宫求解器编写代码的问题。 Node 只是一个带有 next 和 value 的结构体。

func FindExitConcurrently(root []*Node, paths chan []string, path []string, index int) {
    for _, node := range root {
        if node.Value == consts.SUCCESS {
            paths <- path
            return
        }
        if node.Next == nil {
            path = nil
            return
        }
        path = append(path, node.Value)
        index++
        FindExitConcurrently(node.Next, paths, path, index)
        path = nil
        index--
    }
    if index == 0 {
        close(paths)
    }
}

func FindPathConcurrently(parentNode *Node) {
    var pathChannels []chan []string
    for key, value := range parentNode.Next {
        pathChannels = append(pathChannels, make(chan []string))
        go FindExitConcurrently(value.Next, pathChannels[key], nil, 0)
    }
    cases := make([]reflect.SelectCase, len(pathChannels))
    for i, ch := range pathChannels {
        cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)}
    }

    remaining := len(cases)
    for remaining > 0 {
        chosen, value, ok := reflect.Select(cases)
        if !ok {
            // The chosen channel has been closed, so zero out the channel to disable the case
            cases[chosen].Chan = reflect.ValueOf(nil)
            remaining -= 1
            continue
        }
        //fmt.Printf("Read from channel %#v and received %s\n", pathChannels[chosen], value.String())
        fmt.Println(value)
    }
}

【问题讨论】:

    标签: go goroutine


    【解决方案1】:

    您因未始终关闭通道而造成了死锁。考虑所有从你的goroutines引出的路径(提示:其中一些是return提早,因此跳过close(...)调用)。

    要解决并发问题,请将您最喜欢的 IDE 与 Go 调试器结合使用。带有 Go 扩展的 VSCode 效果很好 - 只需启动一个调试会话,它就会自动中断 panic。然后您可以检查“调用堆栈”窗口以查看每个 goroutine 的实际位置。

    但主要问题是代码过于复杂。你真的只需要一个频道。

    这样的东西应该更具可读性,因此更不容易出错:

    func FindExits(root []*Node, paths chan []string, path []string) {
        for _, node := range root {
            if node.Value == consts.SUCCESS {
                paths <- path
                return
            }
            FindExits(node.Next, paths, append(path, node.Value))
            if path != nil {
                path = append([]string{}, path...) // detach the slice
            }
        }
    }
    
    func FindExitConcurrently(root []*Node, paths chan []string, wg *sync.WaitGroup) {
        FindExits(root, paths, nil)
        wg.Done()
    }
    
    func FindPathConcurrently(parentNode *Node) {
        pathCh := make(chan []string)
        var wg sync.WaitGroup
        for _, next := range parentNode.Next {
            wg.Add(1)
            go FindExitConcurrently(next.Next, pathCh, &wg)
        }
        go func() {
            for value := range pathCh {
                fmt.Println(strings.Join(value, ","))
            }
        }()
        wg.Wait()
        close(pathCh)
    }
    

    【讨论】:

    • 有足够大的缓冲通道,wg.Wait() 的退出条件不足go.dev/play/p/6uXQJQt1-9W
    • 当然,这就是我没有使用缓冲通道的原因。一般来说,缓冲通道是一个高级主题,应该谨慎使用。此处无需缓冲。
    猜你喜欢
    • 1970-01-01
    • 2015-04-02
    • 2015-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-22
    • 1970-01-01
    相关资源
    最近更新 更多