我需要开始大量的goroutines并等待他们的终止。直观的方式似乎是使用一个频道等到所有这些都完成了:
package main
type Object struct {
//data
}
func (obj *Object) Update(channel chan int) {
//update data
channel <- 1
return
}
func main() {
channel := make(chan int, n)
list := make([]Object, n, m)
for {
for _, object := range list {
go object.Update(channel)
}
for i := 0; i < n; i++ {
<-channel
}
//now everything has been updated. start again
}
}
但问题是对象的数量以及因此可能会改变goroutines的数量。是否可以更改通道的缓冲区大小?
是否有更优雅的方法可以做到这一点?
答案 0 :(得分:30)
我使用WaitGroup作为此问题的解决方案。翻译您当前的代码,使用一些日志来明确发生了什么:
package main
import "sync"
import "fmt"
import "time"
type Object struct {
//data
}
func (obj *Object) Update(wg *sync.WaitGroup) {
//update data
time.Sleep(time.Second)
fmt.Println("Update done")
wg.Done()
return
}
func main() {
var wg sync.WaitGroup
list := make([]Object, 5)
for {
for _, object := range list {
wg.Add(1)
go object.Update(&wg)
}
//now everything has been updated. start again
wg.Wait()
fmt.Println("Group done")
}
}
答案 1 :(得分:4)
这项任务并非完全无足轻重,编写一辆越野车很容易。我建议在stdlib中使用现成的解决方案 - sync.WaitGroup
。引用链接:
WaitGroup等待完成goroutine的集合。主goroutine调用Add来设置要等待的goroutines的数量。然后每个goroutine运行并在完成后调用Done。同时,Wait可以用来阻止所有goroutine完成。
答案 2 :(得分:0)
@tjameson很好地解释了如何使用WaitGroup
,如何将对WaitGroup
对象的引用传递给您的函数。当你defer
时,我对他的例子做出的一个改变是杠杆Done
。我认为这个defer ws.Done()
应该是你函数中的第一个语句。
我喜欢WaitGroup
的简单。但是,我不喜欢我们需要将引用传递给goroutine,因为这意味着并发逻辑将与您的业务逻辑混合。
所以我想出了这个通用函数来解决这个问题:
// Parallelize parallelizes the function calls
func Parallelize(functions ...func()) {
var waitGroup sync.WaitGroup
waitGroup.Add(len(functions))
defer waitGroup.Wait()
for _, function := range functions {
go func(copy func()) {
defer waitGroup.Done()
copy()
}(function)
}
}
所以你的例子可以通过这种方式解决:
type Object struct {
//data
}
func (obj *Object) Update() {
//update data
time.Sleep(time.Second)
fmt.Println("Update done")
return
}
func main() {
functions := []func(){}
list := make([]Object, 5)
for _, object := range list {
function := func(obj Object){ object.Update() }(object)
functions = append(functions, function)
}
Parallelize(functions...)
fmt.Println("Group done")
}
如果您想使用它,可以在https://github.com/shomali11/util
找到它