当我关闭通道并等待后,为什么会出现所有goroutine都处于休眠状态的情况?

huangapple go评论90阅读模式
英文:

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链接的所有代码

Playground链接

英文:

Following is the code:

func makeData() map[string][]Data {
m := make(map[string][]Data)
s := &quot;abcdefghijklmno&quot;
for i, c := range s {
data := []Data{
{value: &quot;hey_&quot; + string(c), id: i * i},
{value: &quot;hello_&quot; + string(c) + string(c), id: i + i},
{value: &quot;bye_&quot; + string(c), id: i + 1},
}
m[strconv.Itoa(i)] = data
}
return m
}
func process(key string, value []Data) (*Result, error) {
if key == &quot;hey_a&quot; {
return nil, errors.New(&quot;error&quot;)
}
res := Result{data: Data{value: &quot;hi&quot;, id: 0}, id: 1}
return &amp;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 &lt;- StringAndData{
str:  key,
data: res.data,
}
return nil
}
sliceChan &lt;- res
return nil
}(key, value)
})
}
if err := errg.Wait(); err != nil {
fmt.Println(&quot;error&quot;)
} else {
fmt.Println(&quot;success&quot;)
}
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
}

Playground

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")

}

基本上,我改变了从 mapChansliceChan 通道中读取值的方式。这是在单独的 goroutine 中完成的,因此读取这些通道不会被阻塞。

添加了 mapDonesliceDone 通道,只是为了确保在 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 to mapChan, because you never get to read from it (because it is below the errg.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 &lt;- true
}()
go func(){
for ac := range sliceChan {
fmt.Println(ac)
}
sliceDone &lt;- 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 &lt;- StringAndData{
str:  key,
data: res.data,
}
return nil
}
sliceChan &lt;- res
return nil
}(key, value)
})
}
if err := errg.Wait(); err != nil {
fmt.Println(&quot;error&quot;)
} else {
fmt.Println(&quot;success&quot;)
}
close(mapChan)
close(sliceChan)
&lt;-mapDone
&lt;-sliceDone
fmt.Println(&quot;finished&quot;)
}

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.

huangapple
  • 本文由 发表于 2021年6月9日 18:33:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/67902394.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定