Go协程有时候能正常工作,有时候会出现关闭通道错误。

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

Go Routines sometimes work, sometimes gives close channel error

问题

我是你的中文翻译助手,以下是代码的翻译:

package main

import (
	"fmt"
	"time"
	"sync"
)

func JobsDispatcher(in chan int, data []int){
	for _, value := range data{
		in<-value
	}
	close(in)
}

func Worker(in chan int, out chan int, wg *sync.WaitGroup){
	wg.Add(1)
	for{
		inMsg, ok := <-in
		if !ok{
			wg.Done()
			return
		}
		out <- inMsg
	}

}

func PrintInt(out chan int){
	for {
		outMsg, ok := <-out
		if !ok{
			fmt.Println("")
			fmt.Println("That's it")
			return
		}
		fmt.Println(outMsg)
	}
}

func ParallelPrint(data []int){
	var wg sync.WaitGroup

	in := make(chan int)
	out := make(chan int)

	parallelStartTime := time.Now()

	go JobsDispatcher(in, data)

	for i:=0;i<5;i++{
		go Worker(in,out,&wg)
	}


	go func(){
		wg.Wait()
		close(out)
	}()
	PrintInt(out)

	fmt.Println(time.Since(parallelStartTime))

}

func main(){
	data := make([]int,0)
	for i:=0;i<10000;i++{
		data = append(data, i)
	}

	ParallelPrint(data)
}

这段代码是一个并发模式的示例。当运行这段代码时,有时会得到预期的结果(一个从0到9999的完整数字数组),有时只会得到一个显示时间的消息“That's it”,有时会出现“Sending on closed channel”的错误。可能出现了什么问题呢?

英文:

I'm new to Go and I'm trying to learn about the concurrency patterns. When I run the following code, I sometimes get the expected results (a full array of numbers from 0 to 9999). Other times I just get a "That's it" message with the time displayed. And sometimes I just get a "Sending on closed channel" error. What could be going wrong here?

package main
import (
&quot;fmt&quot;
&quot;time&quot;
&quot;sync&quot;
)
func JobsDispatcher(in chan int, data []int){
for _, value := range data{
in&lt;-value
}
close(in)
}
func Worker(in chan int, out chan int, wg *sync.WaitGroup){
wg.Add(1)
for{
inMsg, ok := &lt;-in
if !ok{
wg.Done()
return
}
out &lt;- inMsg
}
}
func PrintInt(out chan int){
for {
outMsg, ok := &lt;-out
if !ok{
fmt.Println(&quot;&quot;)
fmt.Println(&quot;That&#39;s it&quot;)
return
}
fmt.Println(outMsg)
}
}
func ParallelPrint(data []int){
var wg sync.WaitGroup
in := make(chan int)
out := make(chan int)
parallelStartTime := time.Now()
go JobsDispatcher(in, data)
for i:=0;i&lt;5;i++{
go Worker(in,out,&amp;wg)
}
go func(){
wg.Wait()
close(out)
}()
PrintInt(out)
fmt.Println(time.Since(parallelStartTime))
}
func main(){
data := make([]int,0)
for i:=0;i&lt;10000;i++{
data = append(data, i)
}
ParallelPrint(data)
}

答案1

得分: 5

这个很简单。这就是为什么你永远不要在 goroutine 中使用 WaitGroup 的 Add 方法。在启动 goroutine 之前,总是要先调用它。

问题在于你堆积了一堆 goroutine,然后立即调用了 Wait。Go 并不保证在任何特定的时间运行你的 goroutine,就像 POSIX 或 Windows 线程一样没有保证。

所以,在这种情况下,你给调度器一堆将来要运行的 goroutine,但它决定先完成你的代码。因此,在执行 wg.Wait()close(out) 之前,它先执行了 wg.Add()

英文:

This one is easy. This is why you never use WaitGroup's Add in a goroutine. Always call it before starting a goroutine.

The problem is that you stack up a bunch of goroutine's and then call Wait immediately. Go does not promise to run your goroutines at any particular time, just like POSIX or Windows threads are not guaranteed.

So, in this case, you gave the scheduler a bunch of goroutines to run in the future, but it decided to finish your code first. So it ran wg.Wait() and close(out) before ever doing wg.Add().

答案2

得分: 4

你想要在goroutine之外调用wg.Add,即:

for i := 0; i < 5; i++ {
    wg.Add(1)    // 在这里,而不是在Worker()函数内部
    go Worker(in, out, &wg)
}

否则,它可能会在任何工作程序调用wg.Add之前添加所有工作程序,然后在任何工作程序调用wg.Add之前就会触发wg.Wait,导致立即返回并关闭通道。

英文:

You want to call wg.Add outside the goroutine it's for - i.e.:

for i:=0;i&lt;5;i++{
wg.Add(1)    // Here, not inside Worker()
go Worker(in,out,&amp;wg)
}

Otherwise, it can add all the workers, hit the wg.Wait before any workers have called wg.Add, which will return immediately, then close the channel.

huangapple
  • 本文由 发表于 2017年8月16日 01:55:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/45698651.html
匿名

发表评论

匿名网友

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

确定