Wait for n goroutines to finish

I need to start a huge number of goroutines and wait for them to complete. Apparently, the intuitive way uses the channel to wait for the completion of all these actions:

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 } } 

But the problem is that the number of objects and, therefore, the number of mountains can change. Is it possible to change the channel buffer size?

Is there a more elegant way to do this?

+7
source share
3 answers

I used WaitGroup as a solution to this problem. Translating your current code using some logs to understand what is going on:

 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") } } 
+27
source

This task is not entirely trivial, it is quite easy to write buggies. I recommend using a ready-made solution in stdlib - sync.WaitGroup . Quote from the link:

A WaitGroup waits for the completion of the goroutines set. The main goroutine calls Add to set the number of pending goroutines. Then each of the goroutines starts up and calls Done when done. At the same time, Wait can be used to block until all goroutines are complete.

+4
source

@tjameson did a great job explaining how to use WaitGroup , how to pass a reference to your WaitGroup object to your function. The only change I would make with his example is the defer lever when you are Done . I think this defer ws.Done() should be the first statement in your function.

I like WaitGroup simplicity. However, I do not like that we need to pass a link to goroutine, because this will mean that the concurrency logic will be mixed with your business logic.

So, I came up with this general function to solve this problem for me:

 // 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) } } 

So your example can be solved as follows:

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

If you want to use it, you can find it here https://github.com/shomali11/util

0
source

All Articles