让golang在所有goroutine完成后关闭已使用的通道。

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

Let golang close used channel after all goroutines finished

问题

我正在尝试运行一些goroutine,并将它们的结果发送到一个通道。我需要一种好的方法,在所有goroutine完成后关闭通道。

我的第一次尝试是在生成所有goroutine后关闭通道,但我觉得在所有goroutine发送结果之前通道就关闭了。

for i := 0; i <= 10; i++ {
  go func() {
    result := calculate()
    c <- result
  }()
}
close(c)
for result := range c {
  all_result = append(all_result, result...)
}

然后,我尝试了第二种方法,计数线程并在没有线程运行时关闭通道。

for i := 0; i <= 10; i++ {
  go func() {
    atomic.AddUint64(&go_routine_count, 1)
    result := calculate()
    c <- result
    atomic.AddUint64(&rt_count, ^uint64(0))
  }()
}
go func() {
  for {
    // 一些小时间,让上面的goroutine在此goroutine实际检查go_routine_count==0之前计数go_routine_count
    time.Sleep(time.Millisecond)
    go_current_routine_count := atomic.LoadUint64(&go_routine_count)
    if go_routine_count == 0 {
      close(c)
    }
  }
}()
for result := range c {
  all_result = append(all_result, result...)
}

这个方法可以工作,但我觉得可能有更正确或更高效的方法。而且,如果后面的计数检查的goroutine在循环中的goroutine之前运行,这种方法就不起作用。

有更好的方法吗?

英文:

I am trying to run a number of goroutines which will give their results to a channel. I need a good way to let channel close after all goroutines are done.

My first try is to close it after spawn all go routines but I think somehow the channel is closed before all goroutines can send their results.

for i:=0; i&lt;=10;i++{
  go func(){
    result:=calculate()
    c&lt;-result
  }()
}
close(c)
for result:= range c{
  all_result=append(all_result, result...)
}

Then, my second try I come up with counting a thread and close it after no thread is running.

for i:=0; i&lt;=10;i++{
  go func(){
    atomic.AddUint64(&amp;go_routine_count, 1)
    result:=calculate()
    c&lt;-result
    atomic.AddUint64(&amp;rt_count, ^uint64(0))
  }()
}
go func(){
  for{
    // some little time to let above goroutine count up go_routine_count before this goroutine can actually check go_routine_count==0
    time.Sleep(time.Millisecond)
    go_current_routine_count:=atomic.LoadUint64(&amp;go_routine_count)
    if go_routine_count==0{
      close(c)
    }
  }
}()
for result:= range c{
  all_result=append(all_result, result...)
}

It works but I feel there might be more correct or more efficient way. Also, somehow in some case if the later goroutine for count check is run before the goroutines in loop, this method won't work.

Is there a better way?

答案1

得分: 29

sync.WaitGroup 类型应该封装你想要做的事情,而不需要使用 sleep 调用或忙等待。它允许你等待任意数量的任务完成,而不用担心它们完成的顺序。

以你的原始示例为例,你可以修改它以使用一个等待组:

    var wg sync.WaitGroup
    for i := 0; i <= 10; i++ {
        wg.Add(1)
        go func(){
            result := calculate()
            c <- result
            wg.Done()
        }()
    }

    // 当所有 goroutine 完成时关闭通道
    go func() {
        wg.Wait()
        close(c)
    }()

    for result := range c {
        all_result = append(all_result, result...)
    }
英文:

The sync.WaitGroup type should encapsulate what you want to do, without needing sleep calls or busy waiting. It allows you to wait on an arbitrary number of tasks, not worrying about in which order they complete.

Taking your original example, you could alter it to use a wait group like so:

    var wg sync.WaitGroup
    for i := 0; i &lt;= 10; i++ {
        wg.Add(1)
        go func(){
            result := calculate()
            c &lt;- result
            wg.Done()
        }()
    }

    // Close the channel when all goroutines are finished
    go func() {
        wg.Wait()
        close(c)
    }()

    for result := range c {
        all_result = append(all_result, result...)
    }

huangapple
  • 本文由 发表于 2014年2月17日 10:14:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/21819622.html
匿名

发表评论

匿名网友

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

确定