Правильно ли этот пример использования sync.WaitGroup
? Это дает ожидаемый результат, но я не уверен насчет положения wg.Add(4)
и wg.Done()
. Имеет ли смысл добавлять сразу четыре горутины 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
defer wg.Done()
вызов в начале функции.Ответы:
Да, этот пример верен. Важно, чтобы это произошло
wg.Add()
передgo
оператором, чтобы предотвратить состояние гонки. Также будет правильным следующее:Однако звонить
wg.Add
снова и снова, когда вы уже знаете, сколько раз он будет вызван, довольно бессмысленно .Waitgroups
паника, если счетчик упадет ниже нуля. Счетчик начинается с нуля, каждыйDone()
равен a,-1
и каждыйAdd()
зависит от параметра. Итак, чтобы счетчик никогда не опускался ниже, и чтобы избежать паники, вам нужно,Add()
чтобы он гарантированно пришел раньшеDone()
.В Go такие гарантии дает модель памяти .
Модель памяти утверждает, что все операторы в одной горутине выполняются в том же порядке, в котором они написаны. Возможно, они на самом деле не будут в таком порядке, но результат будет таким, как если бы он был. Также гарантируется, что горутина не запустится до тех пор, пока не будет вызвана
go
инструкция, которая ее вызывает . Поскольку операторAdd()
встречается передgo
оператором, аgo
оператор - перед операторомDone()
, мы знаем, что этоAdd()
происходит перед операторомDone()
.Если вы поставили этот
go
оператор передAdd()
, программа может работать правильно. Однако это будет состояние гонки, потому что это не будет гарантировано.источник
defer wg.Done()
мы были уверены, что он будет вызван независимо от маршрута, по которому идет горутина? Спасибо.defer
и одна из ваших горутин не запускаетсяwg.Done()
... не будет ли вашаWait
просто заблокирована навсегда? Похоже, это может легко внести в ваш код труднообнаружимую ошибку ...Я бы порекомендовал встроить
wg.Add()
вызов вdoSomething()
саму функцию, чтобы, если вы измените количество раз, которое он вызывается , вам не нужно отдельно настраивать параметр добавления вручную, что может привести к ошибке, если вы обновите один, но забудете обновить другое (в этом тривиальном примере это маловероятно, но все же я лично считаю, что это лучшая практика для повторного использования кода).Как указывает Стивен Вайнберг в своем ответе на этот вопрос , вам нужно увеличить группу ожидания до создания gofunc, но вы можете легко добиться этого, заключив порождение gofunc внутри самой
doSomething()
функции, например:Затем вы можете вызвать его без
go
вызова, например:В качестве игровой площадки: http://play.golang.org/p/WZcprjpHa_
источник
источник