英文:
Why am I getting all goroutines are asleep when I close the channel after waiting?
问题
以下是代码的翻译:
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语言中的通道和并发编程还不熟悉,希望能得到帮助!
编辑
添加了来自playground链接的所有代码
英文:
Following is the code:
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
}
I am getting fatal error: all goroutines are asleep - deadlock!
but I am closing the channels after errg.Wait()
and am not able to understand the reason.
I am trying to print values that I get from the channel after closing them using range
.
I am new to channels and concurrency with go and would appreciate any help!
Edit
Added all the code from the playground link
答案1
得分: 2
看着你的代码,有两个原因可能导致死锁:
errg.Wait()
阻塞了主 goroutine 的执行,直到所有初始化的 goroutine 完成。然而,每个 goroutine 在尝试写入mapChan
时都被阻塞,因为你从未读取它(因为它在errg.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")
}
基本上,我改变了从 mapChan
和 sliceChan
通道中读取值的方式。这是在单独的 goroutine 中完成的,因此读取这些通道不会被阻塞。
添加了 mapDone
和 sliceDone
通道,只是为了确保在 main
goroutine 完成之前读取了所有数据。
英文:
Looking at your code, two things can cause a deadlock:
errg.Wait()
blocks the execution of the main goroutine until all initialized goroutines are finished. However, each goroutine is blocked when trying to write tomapChan
, because you never get to read from it (because it is below theerrg.Wait()
).- You never read from
sliceChan
, so that is a potential deadlock right there.
Here is the link to the modified Playground code, but most of the changes are in the main
function.
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")
}
Basically, I changed how the values are read from mapChan
and sliceChan
channels. This is done in separate goroutines, so reading from these channels is not blocked.
The mapDone
and sliceDone
channels are added just to make sure all of the data is read before the main
goroutine finishes.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论