go - 我怎样才能避免死锁

标签 go

请看下面的代码片段。

package main

import (
    "errors"
    "fmt"
    "math/rand"
    "runtime"
    "sync"
    "time"
)

func random(min, max int) int {
    rand.Seed(time.Now().Unix())
    return rand.Intn(max-min) + min
}

func err1(rand int, chErr chan error, wg *sync.WaitGroup) {
    if rand == 1 {
        chErr <- errors.New("Error 1")
    }

    wg.Done()

}

func err2(rand int, chErr chan error, wg *sync.WaitGroup) {
    if rand == 2 {
        chErr <- errors.New("Error 2")
    }
    wg.Done()
}

func err3(rand int, chErr chan error, wg *sync.WaitGroup) {
    if rand == 3 {
        chErr <- errors.New("Error 3")
    }
    wg.Done()
}

func err4(rand int, chErr chan error, wg *sync.WaitGroup) {
    if rand == 3 {
        chErr <- errors.New("Error 4")
    }
    wg.Done()
}

func err5(rand int, chErr chan error, wg *sync.WaitGroup) {
    if rand == 4 {
        chErr <- errors.New("Error 5")
    }
    wg.Done()
}

func main() {

    runtime.GOMAXPROCS(runtime.NumCPU())

    chErr := make(chan error, 1)
    wg := new(sync.WaitGroup)

    //n := random(1, 8)
    n := 3
    fmt.Println(n)

    wg.Add(5)
    go err1(n, chErr, wg)
    go err2(n, chErr, wg)
    go err3(n, chErr, wg)
    go err4(n, chErr, wg)
    go err5(n, chErr, wg)

    fmt.Println("Wait")
    wg.Wait()
    select {
    case err := <-chErr:
        fmt.Println(err)
        close(chErr)
    default:
        fmt.Println("NO error, job done")
    }
}

如何避免这里出现死锁?我可以分配缓冲区长度 2,但也许它有更优雅的方法来解决问题。

我有意识地对函数 err3 和 err4 执行了 rand == 3。

最佳答案

您的程序已死锁,因为您的 channel 已满。

您的 channel 大小为 1。然后调用 wg.Wait() .. 等待调用 5 个函数。现在,一旦您到达 err3 .. rand == 3,因此在您的 channel 上传递了一个错误。

此时,您的 channel 已满,您只勾选了 3 个 WaitGroup 项目。

err4 以值 3 .. 调用,它也想在您的 channel 上放一个错误。此时,它会阻塞 - 因为您的 channel 已满并且没有任何内容从中弹出。

所以你的主 goroutine 会阻塞,因为你的 WaitGroup 永远不会完成。

修复确实是让您的 channel 缓冲区更大。这样,当错误试图放置在 channel 上时 - 它不会阻塞,并且您的 WaitGroup 有机会勾选其所有项目。

关于go - 我怎样才能避免死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28063465/

相关文章:

amazon-web-services - 运行代码部署 Hook 时找不到 Go 命令

Go - 如何组合多个字符串输出唯一的 'Compressed' 字符串?

csv - 如何从 S3 读取 CSV 文件

go - 与Go和PostgreSQL不同的排序顺序

json - 将 slice 结果 JSON 插入 MongoDB

go - 来自不同文件的全局变量 Golang

mysql - Gin + Golang + 数据库连接池

go - go channel 和 goroutines 如何转换为 webassembly?

go - 如何检查链接到我的 Go 代码中的包的大小

go - 将值转换为结构