【问题标题】:fatal error all goroutines are asleep - deadlock致命错误所有 goroutines 都睡着了 - 死锁
【发布时间】:2021-05-18 07:00:47
【问题描述】:

我正在使用缓冲通道,我得到了我需要的正确输出。

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

type Expert struct {
    name string
    age  int
}

func main() {
    fmt.Println("==== GoRoutines ====")
    expertChannel := make(chan Expert, 3)
    wg.Add(1)
    go printHello()
    wg.Add(1)
    go addDataToChannel(expertChannel, "Name", 24)
    wg.Add(1)
    go addDataToChannel(expertChannel, "Name", 24)
    wg.Add(1)
    go addDataToChannel(expertChannel, "Name", 24)
    wg.Wait()
    close(expertChannel)
    for x := range expertChannel {
        fmt.Println("Expert Data :: ", x)
    }

}

func printHello() {
    for i := 1; i <= 5; i++ {
        fmt.Println("This is from PrintHello() Function where i = ", i)
    }
    defer wg.Done()

}

func addDataToChannel(c chan Expert, name string, age int) {
    defer wg.Done()

    c <- Expert{
        name,
        age,
    }
}

但是当我使用无缓冲通道时,我得到了错误,那就是 致命错误:所有 goroutine 都处于休眠状态 - 死锁! 为什么会发生这种情况以及如何解决?

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

type Expert struct {
    name string
    age  int
}

func main() {
    fmt.Println("==== GoRoutines ====")
    expertChannel := make(chan Expert)
    wg.Add(1)
    go printHello()
    wg.Add(1)
    go addDataToChannel(expertChannel, "Name", 24)
    wg.Add(1)
    go addDataToChannel(expertChannel, "Name", 24)
    wg.Add(1)
    go addDataToChannel(expertChannel, "Name", 24)
    wg.Wait()
    close(expertChannel)
    for x := range expertChannel {
        fmt.Println("Expert Data :: ", x)
    }

}

func printHello() {
    for i := 1; i <= 5; i++ {
        fmt.Println("This is from PrintHello() Function where i = ", i)
    }
    defer wg.Done()

}

func addDataToChannel(c chan Expert, name string, age int) {
    defer wg.Done()

    c <- Expert{
        name,
        age,
    }
}

什么时候使用缓冲通道,什么时候使用非缓冲通道如何识别这两个通道类别的用例?

【问题讨论】:

    标签: go concurrency


    【解决方案1】:

    如果缓冲区已满,则通过通道发送和接收会阻塞。对于无缓冲通道,因为它没有缓冲区,除非在另一端读取数据,否则它将立即阻塞。

    一旦您将第一个数据发送到通道,除非您阅读,否则其他例程将没有空间将数据发送到通道。所以发件人被屏蔽了。

    您需要解除阻塞从通道读取的主程序。这样发送方就会找到空间继续向通道发送数据。

    现在 wg.Wait() 处于阻塞状态,不允许主例程(for 循环)从通道读取。一旦它开始从通道读取,被阻止的发送者也可以恢复并发送更多数据。

    在并发 go 例程中执行 wg.Wait():

    go func() {
        wg.Wait()
        close(expertChannel)
    }()
    

    【讨论】:

    • 我不明白为什么 wg.Wait() 会阻塞主程序?如果我使用您提到的代码,那么它会很好,那么为什么不声明新的 goroutine func 就不能工作?
    • wg.Wait() 在主程序中被调用。 Wait() 阻塞,直到 WaitGroup 计数器为 0。当所有预期的例程都调用 wg.Done() 时,它将为 0。其他例程无法调用 wg.Done(),因为它们在通道上发送数据时被阻塞。
    • 通过允许for x := range expertChannel 运行,您将确保将值拉出通道,以便发送者不会被阻止发送给它。
    猜你喜欢
    • 2016-04-06
    • 2023-02-01
    • 2020-07-16
    • 1970-01-01
    • 1970-01-01
    • 2012-01-06
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多