为什么我在等待后关闭通道时让所有 goroutines 都睡着了?
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()
之后关闭了频道,我无法理解原因。
我正在尝试打印使用 [=13=].
关闭通道后从通道中获取的值
我是 go 的通道和并发的新手,非常感谢任何帮助!
编辑
添加了 playground link
中的所有代码
查看您的代码,有两件事可能导致死锁:
errg.Wait()
阻塞主 goroutine 的执行,直到所有初始化的 goroutine 完成。但是,每个 goroutine 在尝试写入 mapChan
时都会被阻塞,因为您永远无法读取它(因为它在 errg.Wait()
之下)。
- 你从来没有从
sliceChan
读取过,所以这是一个潜在的死锁。
Here是修改后的Playground代码link,但大部分改动都在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
通道读取值的方式。这是在单独的 goroutines 中完成的,因此不会阻止从这些通道读取。
添加 mapDone
和 sliceDone
通道只是为了确保在 main
goroutine 完成之前读取所有数据。
代码如下:
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()
之后关闭了频道,我无法理解原因。
我正在尝试打印使用 [=13=].
关闭通道后从通道中获取的值我是 go 的通道和并发的新手,非常感谢任何帮助!
编辑 添加了 playground link
中的所有代码查看您的代码,有两件事可能导致死锁:
errg.Wait()
阻塞主 goroutine 的执行,直到所有初始化的 goroutine 完成。但是,每个 goroutine 在尝试写入mapChan
时都会被阻塞,因为您永远无法读取它(因为它在errg.Wait()
之下)。- 你从来没有从
sliceChan
读取过,所以这是一个潜在的死锁。
Here是修改后的Playground代码link,但大部分改动都在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
通道读取值的方式。这是在单独的 goroutines 中完成的,因此不会阻止从这些通道读取。
添加 mapDone
和 sliceDone
通道只是为了确保在 main
goroutine 完成之前读取所有数据。