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

我是 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并懒惰地初始化每个队列。


慕丝7291255
浏览 68回答 2
2回答

叮当猫咪

OP更新问题后更新答案您可以只循环地图中的所有键,也许还有另一个 goroutine 不断循环所有键。显然,如果一个键没有被初始化,那么它就不会出现在 for range 循环中。对于每个键,您可以启动一个监听的 goroutine,这样它就不会阻塞,或者您可以使用缓冲通道,这样它们就不会阻塞到缓冲区限制。您也可以最好使用 waitGroup,而不是 time.Sleep(),这些仅用于这个简单的示例。package mainimport ("fmt"&nbsp; &nbsp; "time")func main() {&nbsp; &nbsp; m := make(map[int]chan string)&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; m[0] = make(chan string)&nbsp; &nbsp; &nbsp; &nbsp; m[0] <- "abab"&nbsp; &nbsp; }()&nbsp; &nbsp; time.Sleep(time.Second * 1)&nbsp; //sleep so the above goroutine initializes the key 0 channel&nbsp; &nbsp; for key := range m{&nbsp; &nbsp; &nbsp; //loop on all non-nil keys&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(key)&nbsp; &nbsp; &nbsp; &nbsp; go func(k int){&nbsp; &nbsp; &nbsp; &nbsp; // goroutine to listen on this channel&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(<- m[k])&nbsp; &nbsp; &nbsp; &nbsp; }(key)&nbsp; &nbsp; }&nbsp; &nbsp; time.Sleep(time.Second * 1) //sleep so u can see the effects of the channel recievers}&nbsp;&nbsp;旧答案流量就是这样。主 goroutine 启动。地图已创建。主 goroutine 遇到另一个 goroutine。它产生了 goroutine 并继续它的生活。然后遇到这条线,fmt.Println(<-m[0]),这是一个问题,因为地图确实初始化了,但是地图本身的通道没有初始化!当主 goroutine 到达fmt.Println(<-m[0])时,另一个 goroutine 还没有初始化通道!所以这是一个简单的修复,只需在生成 goroutine 之前初始化通道,你就可以开始了!package mainimport "fmt"func main() {&nbsp; &nbsp; m := make(map[int]chan string)&nbsp; &nbsp; m[0] = make(chan string)&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; m[0] <- "abab"&nbsp; &nbsp; }()&nbsp; &nbsp; fmt.Println(<-m[0])}编辑:请注意这fmt.Println(<-m[0])是阻塞的,这意味着如果在另一个 goroutine 中,您不在通道上发送,您也会陷入死锁,因为您试图在没有人实际发送的情况下在通道上接收。

慕村225694

您需要同步通道的创建。就目前而言,您的主线程到达时<-m[0]仍然m[0]是一个未初始化的通道,并且在未初始化的通道上接收永远阻塞。您的 go 例程创建了一个新通道并将其放入m[0],但主要的 go 例程已经在侦听先前的零值。在这个新通道上发送也会永远阻塞,因为它没有读取任何内容,所以所有 goroutine 都会阻塞。要解决这个问题,请移到m[0] = make(chan string)你的 go 例程之上,这样它就会同步发生。
打开App,查看更多内容
随时随地看视频慕课网APP