Пример для синхронизации.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") }