Дождитесь окончания Н горутин
Мне нужно запустить огромное количество 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 может измениться. Можно ли изменить размер буфера канала ?
там может быть более элегантный способ сделать это ?
3 ответов
я использовал 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")
}
}
эта задача не совсем тривиальна,довольно легко написать багги. Я рекомендую использовать готовое решение в stdlib -sync.WaitGroup
. Цитата из ссылки:
в WaitGroup ждет коллекция горутин до конца. Основные вызовы goroutine добавить, чтобы установить количество goroutines ждать. Затем каждый из goroutines работает и звонки сделаны, когда закончите. В то же время Wait можно использовать для блокировки, пока все goroutines не законченный.
@tjameson проделал большую работу, объясняя, как использовать WaitGroup
, Как передать ссылку на свой WaitGroup
объект для вашей функции. Единственное изменение, которое я бы сделал в его примере, - это плечо defer
когда вы Done
. Я думаю, это defer ws.Done()
должно быть первым утверждением в вашей функции.
мне нравится WaitGroup
'простоты s. Однако мне не нравится, что нам нужно передать ссылку на 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