¿Este ejemplo de uso es sync.WaitGroup
correcto? Da el resultado esperado, pero no estoy seguro sobre el wg.Add(4)
y la posición de wg.Done()
. ¿Tiene sentido agregar las cuatro gorutinas a la vez 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 (como se esperaba):
Function in background, duration: 150ms
Function in background, duration: 200ms
Function in background, duration: 400ms
Function in background, duration: 600ms
Done
defer wg.Done()
llamada inicial al inicio de la función.Respuestas:
Sí, este ejemplo es correcto. Es importante que
wg.Add()
ocurra antes de lago
declaración para evitar condiciones de carrera. Lo siguiente también sería correcto:Sin embargo, no tiene sentido llamar
wg.Add
una y otra vez cuando ya sabe cuántas veces se llamará.Waitgroups
pánico si el contador cae por debajo de cero. El contador comienza en cero, cada unoDone()
es un-1
y cada unoAdd()
depende del parámetro. Por lo tanto, para asegurarse de que el mostrador nunca caiga por debajo y evitar el pánico, debeAdd()
tener la garantía de llegar antes que elDone()
.En Go, estas garantías vienen dadas por el modelo de memoria .
El modelo de memoria establece que todas las declaraciones en una sola goroutine parecen ejecutarse en el mismo orden en que están escritas. Es posible que en realidad no estén en ese orden, pero el resultado será como si lo fuera. También se garantiza que una goroutine no se ejecuta hasta después de la
go
declaración que la llama . Dado queAdd()
ocurre antes delgo
enunciado y elgo
enunciado ocurre antes delDone()
, sabemos queAdd()
ocurre antes delDone()
.Si tuviera la
go
declaración antes delAdd()
, el programa puede funcionar correctamente. Sin embargo, sería una condición de carrera porque no estaría garantizada.fuente
defer wg.Done()
estar seguros de que se llama independientemente de la ruta que tome la goroutine? Gracias.defer
y una de sus goroutines no llamawg.Done()
... ¿noWait
se bloqueará simplemente para siempre? Esto parece que podría introducir fácilmente un error difícil de encontrar en su código ...Recomendaría incrustar la
wg.Add()
llamada en ladoSomething()
función en sí, de modo que si ajusta el número de veces que se llama, no tiene que ajustar por separado el parámetro de adición manualmente, lo que podría provocar un error si actualiza uno pero se olvida de actualizar el otro (en este ejemplo trivial eso es poco probable, pero aún así, personalmente creo que es una mejor práctica para la reutilización del código).Como señala Stephen Weinberg en su respuesta a esta pregunta , debe incrementar el grupo de espera antes de generar el gofunc, pero puede lograr esto fácilmente envolviendo el spawn de gofunc dentro de la
doSomething()
función en sí, así:Entonces puede llamarlo sin la
go
invocación, por ejemplo:Como parque infantil: http://play.golang.org/p/WZcprjpHa_
fuente
fuente