Este exemplo de uso é sync.WaitGroup
correto? Ele dá o resultado esperado, mas não tenho certeza sobre a wg.Add(4)
e a posição de wg.Done()
. Faz sentido adicionar os quatro goroutines de uma vez com 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")
}
Resultado (conforme esperado):
Function in background, duration: 150ms
Function in background, duration: 200ms
Function in background, duration: 400ms
Function in background, duration: 600ms
Done
defer wg.Done()
chamada inicial no início da função.Respostas:
Sim, este exemplo está correto. É importante que
wg.Add()
aconteça antes dago
declaração para evitar condições de corrida. O seguinte também seria correto:No entanto, é inútil ligar
wg.Add
repetidamente quando você já sabe quantas vezes ela será chamada.Waitgroups
entre em pânico se o contador cair abaixo de zero. O contador começa em zero, cadaDone()
um é a-1
e cada umAdd()
depende do parâmetro. Portanto, para garantir que o contador nunca caia abaixo e evitar pânico, você precisaAdd()
ter a garantia de vir antes doDone()
.No Go, essas garantias são dadas pelo modelo de memória .
O modelo de memória afirma que todas as instruções em uma única goroutine parecem ser executadas na mesma ordem em que são escritas. É possível que eles não estejam realmente nessa ordem, mas o resultado será como se estivesse. Também é garantido que um goroutine não será executado até depois da
go
instrução que o chama . Como oAdd()
ocorre antes dago
instrução e ago
instrução ocorre antes deDone()
, sabemos queAdd()
ocorre antes deDone()
.Se você deseja que a
go
instrução venha antes deAdd()
, o programa pode funcionar corretamente. No entanto, seria uma condição de corrida porque não seria garantida.fonte
defer wg.Done()
termos certeza de que ele será chamado independentemente da rota que o goroutine tomar? Obrigado.defer
e um de seus goroutines não ligarwg.Done()
... você não vaiWait
bloquear para sempre? Parece que pode facilmente introduzir um bug difícil de encontrar em seu código ...Eu recomendaria incorporar a
wg.Add()
chamada àdoSomething()
própria função, de modo que se você ajustar o número de vezes que ela é chamada, não precise ajustar separadamente o parâmetro add manualmente, o que pode levar a um erro se você atualizar um, mas se esquecer de atualizar o outro (neste exemplo trivial isso é improvável, mas ainda assim, eu pessoalmente acredito que seja uma prática melhor para reutilização de código).Como Stephen Weinberg aponta em sua resposta a esta pergunta , você tem que incrementar o grupo de espera antes de gerar o gofunc, mas você pode fazer isso facilmente envolvendo o spawn do gofunc dentro da
doSomething()
própria função, assim:Então você pode chamá-lo sem a
go
invocação, por exemplo:Como um playground: http://play.golang.org/p/WZcprjpHa_
fonte
fonte