英文:
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 (
"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)
}
答案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<5;i++{
wg.Add(1) // Here, not inside Worker()
go Worker(in,out,&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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论