为什么这段Golang代码会产生死锁呢?
Why does this Golang code produce deadlock?
我是 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 之前并不是我想要的。我的问题是:有没有办法“动态”创建频道?例如。我有一个 map[int]chan string
类型的映射 m
,我收到的请求包含 id
类型 int
之类的内容。我想通过频道 map[id]
发送消息,但为每个 int
初始化频道的成本太高。我如何 solve/work 解决这个问题?
所以,换句话说,我希望每个 id
都有一个单独的作业队列,并懒惰地初始化每个队列。
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 中,你不在通道上发送,你也会陷入僵局,因为你试图在通道上接收当实际上没有人发送时。
您需要同步创建频道。
就目前而言,您的主线程到达 <-m[0]
而 m[0]
仍然是未初始化的通道,并且在未初始化的通道上接收永远阻塞。
您的 go 例程创建了一个新通道并将其放置在 m[0]
中,但是主要的 go 例程已经在监听先前的零值。在这个新频道上发送也永远阻塞,因为没有从它读取任何东西,所以所有的例程都阻塞。
要解决此问题,请将 m[0] = make(chan string)
移动到您的 go 例程之上,以便它同步发生。
我是 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 之前并不是我想要的。我的问题是:有没有办法“动态”创建频道?例如。我有一个 map[int]chan string
类型的映射 m
,我收到的请求包含 id
类型 int
之类的内容。我想通过频道 map[id]
发送消息,但为每个 int
初始化频道的成本太高。我如何 solve/work 解决这个问题?
所以,换句话说,我希望每个 id
都有一个单独的作业队列,并懒惰地初始化每个队列。
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 中,你不在通道上发送,你也会陷入僵局,因为你试图在通道上接收当实际上没有人发送时。
您需要同步创建频道。
就目前而言,您的主线程到达 <-m[0]
而 m[0]
仍然是未初始化的通道,并且在未初始化的通道上接收永远阻塞。
您的 go 例程创建了一个新通道并将其放置在 m[0]
中,但是主要的 go 例程已经在监听先前的零值。在这个新频道上发送也永远阻塞,因为没有从它读取任何东西,所以所有的例程都阻塞。
要解决此问题,请将 m[0] = make(chan string)
移动到您的 go 例程之上,以便它同步发生。