为了账号安全,请及时绑定邮箱和手机立即绑定

为什么我在等待后关闭频道时,所有 Goroutine 都处于睡眠状态?

为什么我在等待后关闭频道时,所有 Goroutine 都处于睡眠状态?

Go
明月笑刀无情 2022-09-05 15:46:59
代码如下:func makeData() map[string][]Data {    m := make(map[string][]Data)    s := "abcdefghijklmno"    for i, c := range s {        data := []Data{            {value: "hey_" + string(c), id: i * i},            {value: "hello_" + string(c) + string(c), id: i + i},            {value: "bye_" + string(c), id: i + 1},        }        m[strconv.Itoa(i)] = data    }    return m}func process(key string, value []Data) (*Result, error) {    if key == "hey_a" {        return nil, errors.New("error")    }    res := Result{data: Data{value: "hi", id: 0}, id: 1}    return &res, nil}func main() {    runtime.GOMAXPROCS(runtime.NumCPU())    m := makeData()    errg := new(errgroup.Group)    mapChan := make(chan StringAndData)    sliceChan := make(chan *Result)    for key, value := range m {        key := key        value := value        errg.Go(func() error {            return func(key string, value []Data) error {                res, err := process(key, value)                if err != nil {                    return err                }                if res == nil {                    return nil                }                if res.data.id == 1 {                    mapChan <- StringAndData{                        str:  key,                        data: res.data,                    }                    return nil                }                sliceChan <- res                return nil            }(key, value)        })    }    if err := errg.Wait(); err != nil {        fmt.Println("error")    } else {        fmt.Println("success")    }    close(mapChan)    close(sliceChan)    for ac := range mapChan {        fmt.Println(ac.str)    }}type Data struct {    value string    id    int}type Result struct {    data Data    id   int}type StringAndData struct {    str  string    data Data}操场我得到了,但我正在关闭频道之后,我无法理解原因。fatal error: all goroutines are asleep - deadlock!errg.Wait()我正在尝试打印使用 关闭通道后从通道获得的值。range我是 Go 的渠道和并发新手,非常感谢任何帮助!编辑添加了游乐场链接中的所有代码
查看完整描述

1 回答

?
蝴蝶刀刀

TA贡献1801条经验 获得超8个赞

查看代码,有两件事可能会导致死锁:

  • errg.Wait()阻止主 goroutine 的执行,直到所有初始化的 goroutine 都完成。但是,在尝试写入 时,每个 goroutine 都会被阻止,因为您永远无法从中读取(因为它位于 )。mapChanerrg.Wait()

  • 你从来没有读过,所以这是一个潜在的死锁。sliceChan

以下是修改后的 Playground 代码的链接,但大多数更改都在函数中。main

func main() {

    runtime.GOMAXPROCS(runtime.NumCPU())

    m := makeData()

    errg := new(errgroup.Group)


    mapChan := make(chan StringAndData)

    sliceChan := make(chan *Result)

    mapDone := make(chan bool)

    sliceDone := make(chan bool)


    go func(){

        for ac := range mapChan {

            fmt.Println(ac.str)

        }

        mapDone <- true

    }()

    go func(){

        for ac := range sliceChan {

            fmt.Println(ac)

        }

        sliceDone <- true

    }()


    for key, value := range m {

        key := key

        value := value


        errg.Go(func() error {

            return func(key string, value []Data) error {

                res, err := process(key, value)

                if err != nil {

                    return err

                }

                if res == nil {

                    return nil

                }


                if res.data.id == 1 {

                    mapChan <- StringAndData{

                        str:  key,

                        data: res.data,

                    }

                    return nil

                }


                sliceChan <- res

                return nil


            }(key, value)

        })

    }


    if err := errg.Wait(); err != nil {

        fmt.Println("error")

    } else {

        fmt.Println("success")

    }


    close(mapChan)

    close(sliceChan)

    <-mapDone

    <-sliceDone

    fmt.Println("finished")


}

基本上,我更改了从中读取值和通道的方式。这是在单独的goroutines中完成的,因此不会阻止从这些通道读取。mapChansliceChan


添加 和 通道只是为了确保在 goroutine 完成之前读取所有数据。mapDonesliceDonemain


查看完整回答
反对 回复 2022-09-05
  • 1 回答
  • 0 关注
  • 97 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信