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

为什么这段 Golang 代码会产生死锁?

为什么这段 Golang 代码会产生死锁?

Go
慕丝7291255 2022-10-10 16:08:48
我是 Golang 的新手,我很难弄清楚为什么下面的代码会产生死锁。另外,我该如何修复它才能正常工作?    package main        import "fmt"        func main() {        m := make(map[int]chan string)        go func() {            m[0] = make(chan string)            m[0] <- "abab"        }()        fmt.Println(<-m[0])    }编辑:感谢您的回答!m[0]不幸的是,初始化m[0] = make(chan string)在启动一个新的 goroutine 之前并不是我想要的。我的问题是:有没有办法“动态”创建频道?例如,我有一个m类型的地图,map[int]chan string我收到包含类似id类型的请求int。我想通过 channel 发送消息map[id],但是为每个通道初始化通道int成本太高。我该如何解决/解决这个问题?因此,换句话说,我想为每个队列创建一个单独的作业队列,id并懒惰地初始化每个队列。
查看完整描述

2 回答

?
叮当猫咪

TA贡献1776条经验 获得超12个赞

OP更新问题后更新答案


您可以只循环地图中的所有键,也许还有另一个 goroutine 不断循环所有键。显然,如果一个键没有被初始化,那么它就不会出现在 for range 循环中。对于每个键,您可以启动一个监听的 goroutine,这样它就不会阻塞,或者您可以使用缓冲通道,这样它们就不会阻塞到缓冲区限制。您也可以最好使用 waitGroup,而不是 time.Sleep(),这些仅用于这个简单的示例。


package main


import (

"fmt"

    "time"

)

func main() {

    m := make(map[int]chan string)


    go func() {

        m[0] = make(chan string)

        m[0] <- "abab"

    }()


    time.Sleep(time.Second * 1)  //sleep so the above goroutine initializes the key 0 channel


    for key := range m{      //loop on all non-nil keys

        fmt.Println(key)

        go func(k int){        // goroutine to listen on this channel

            fmt.Println(<- m[k])

        }(key)

    }

    time.Sleep(time.Second * 1) //sleep so u can see the effects of the channel recievers



}

  

旧答案


流量就是这样。主 goroutine 启动。地图已创建。主 goroutine 遇到另一个 goroutine。它产生了 goroutine 并继续它的生活。然后遇到这条线,fmt.Println(<-m[0]),这是一个问题,因为地图确实初始化了,但是地图本身的通道没有初始化!当主 goroutine 到达fmt.Println(<-m[0])时,另一个 goroutine 还没有初始化通道!所以这是一个简单的修复,只需在生成 goroutine 之前初始化通道,你就可以开始了!


package main


import "fmt"


func main() {

    m := make(map[int]chan string)

    m[0] = make(chan string)


    go func() {

        m[0] <- "abab"

    }()

    fmt.Println(<-m[0])

}

编辑:请注意这fmt.Println(<-m[0])是阻塞的,这意味着如果在另一个 goroutine 中,您不在通道上发送,您也会陷入死锁,因为您试图在没有人实际发送的情况下在通道上接收。


查看完整回答
反对 回复 2022-10-10
?
慕村225694

TA贡献1880条经验 获得超4个赞

您需要同步通道的创建。

就目前而言,您的主线程到达时<-m[0]仍然m[0]是一个未初始化的通道,并且在未初始化的通道上接收永远阻塞。

您的 go 例程创建了一个新通道并将其放入m[0],但主要的 go 例程已经在侦听先前的零值。在这个新通道上发送也会永远阻塞,因为它没有读取任何内容,所以所有 goroutine 都会阻塞。

要解决这个问题,请移到m[0] = make(chan string)你的 go 例程之上,这样它就会同步发生。


查看完整回答
反对 回复 2022-10-10
  • 2 回答
  • 0 关注
  • 64 浏览
慕课专栏
更多

添加回答

举报

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