这个sync.WaitGroup
的示例用法是否正确?它给出了预期的结果,但我不确定wg.Add(4)
和wg.Done()
的位置。使用wg.Add()
一次添加四个goroutine是否有意义?
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
答案 0 :(得分:135)
是的,这个例子是正确的。重要的是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中,此类保证由memory model提供。
内存模型声明单个goroutine中的所有语句似乎都以与写入时相同的顺序执行。它们可能实际上不是那个顺序,但结果就好像它一样。它也保证goroutine doesn't run until after the go
statement that calls it。由于Add()
发生在go
语句之前,而go
语句发生在Done()
之前,我们知道Add()
发生在Done()
之前。
如果您要在go
之前发出Add()
声明,该程序可能会正常运行。但是,这将是一种竞争条件,因为它不会得到保证。
答案 1 :(得分:24)
我建议将wg.Add()
来电嵌入到doSomething()
功能本身,这样如果您调整其被呼叫的次数,则无需单独调整手动添加参数可能会导致错误,如果你更新一个但忘记更新另一个(在这个不太可能的例子中,但我个人认为这是更好的代码重用实践)。
正如Stephen Weinberg在his answer to this question中指出的那样,你必须在等待组之前增加来产生gofunc,但你可以通过将gofunc spawn包装在{{{}中来实现这一点。 1}}函数本身,像这样:
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
答案 2 :(得分:12)
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") }