Пример для синхронизации.WaitGroup правильно?
это пример использования sync.WaitGroup исправить? Это дает ожидаемый результат, но я не уверен, о wg.Add(4) и позицию wg.Done(). Имеет ли смысл добавить четыре goroutines сразу с wg.Add()?
http://play.golang.org/p/ecvYHiie0P
package main
import (
"fmt"
"sync"
"time"
)
func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
duration := millisecs * time.Millisecond
time.Sleep(duration)
fmt.Println("Function in background, duration:", duration)
wg.Done()
}
func main() {
var wg sync.WaitGroup
wg.Add(4)
go dosomething(200, &wg)
go dosomething(400, &wg)
go dosomething(150, &wg)
go dosomething(600, &wg)
wg.Wait()
fmt.Println("Done")
}
результат (как и ожидалось):
Function in background, duration: 150ms
Function in background, duration: 200ms
Function in background, duration: 400ms
Function in background, duration: 600ms
Done
3 ответа:
Да, этот пример верен. Важно, что
wg.Add()бывает передgoзаявление для предотвращения состояния гонки. Было бы также правильно следующее:func main() { var wg sync.WaitGroup wg.Add(1) go dosomething(200, &wg) wg.Add(1) go dosomething(400, &wg) wg.Add(1) go dosomething(150, &wg) wg.Add(1) go dosomething(600, &wg) wg.Wait() fmt.Println("Done") }впрочем, довольно бессмысленно звонить
wg.Addснова и снова, когда вы уже знаете, сколько раз он будет называться.
Waitgroupsпаника, если счетчик падает ниже нуля. Счетчик начинается с нуля, каждыйDone()это-1и другAdd()зависит от параметр. Таким образом, чтобы убедиться, что счетчик никогда не падает ниже и избежать паники, вам нужноAdd()на гарантированный передDone().в Go такие гарантии дает модель.
модель памяти утверждает, что все операторы в одном goroutine, как представляется, выполняются в том же порядке, как они записаны. Возможно, что они на самом деле не будут в таком порядке, но результат будет таким, как если бы он был. Также гарантируется, что A goroutine не работает до тех пор, пока после
goзаявление, которое называет его. Так какAdd()возникает передgoзаявления иgoутверждение происходит передDone(), мы знаемAdd()возникает передDone().если бы у вас был
goзаявление передAdd()программа может работать правильно. Однако это будет условие гонки, потому что оно не будет гарантировано.
Я бы рекомендовал вставить
wg.Add()вызовdoSomething()сама функция, так что если вы отрегулируете количество раз, когда она вызывается, вам не нужно отдельно настраивать параметр add вручную, что может привести к ошибке, если вы обновите один, но забудете обновить другой (в этом тривиальном примере это маловероятно, но все же я лично считаю, что это лучшая практика для повторного использования кода).как указывает Стивен Вайнберг в его ответ на это вопрос, вы должны увеличить группу ожидания до для нереста gofunc, но вы можете сделать это легко, обернув gofunc икру внутри
doSomething()сама функция, вот так:func dosomething(millisecs time.Duration, wg *sync.WaitGroup) { wg.Add(1) go func() { duration := millisecs * time.Millisecond time.Sleep(duration) fmt.Println("Function in background, duration:", duration) wg.Done() }() }тогда вы можете назвать его без
goссылка, например:func main() { var wg sync.WaitGroup dosomething(200, &wg) dosomething(400, &wg) dosomething(150, &wg) dosomething(600, &wg) wg.Wait() fmt.Println("Done") }как детская площадка:http://play.golang.org/p/WZcprjpHa_
- небольшое улучшение на основе ответа Mroth
- использование отложить для сделано безопаснее
func dosomething(millisecs time.Duration, wg *sync.WaitGroup) { wg.Add(1) go func() { defer wg.Done() duration := millisecs * time.Millisecond time.Sleep(duration) fmt.Println("Function in background, duration:", duration) }() } func main() { var wg sync.WaitGroup dosomething(200, &wg) dosomething(400, &wg) dosomething(150, &wg) dosomething(600, &wg) wg.Wait() fmt.Println("Done") }