Дождитесь окончания Н горутин

Мне нужно запустить огромное количество 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