我试图剥离一组goroutines,然后等待它们全部完成。
import "sync"
func doWork(wg sync.WaitGroup) error {
defer wg.Done()
// Do some heavy lifting... request URL's or similar
return nil
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go doWork(wg)
}
}
但是,当我运行此代码时,我收到以下错误:
fatal error: all goroutines are asleep - deadlock!
goroutine 16 [semacquire]:
sync.runtime_Semacquire(0xc20818c658)
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/sema.goc:199 +0x30
sync.(*WaitGroup).Wait(0xc2080544e0)
/usr/local/Cellar/go/1.3/libexec/src/pkg/sync/waitgroup.go:129 +0x14b
main.main()
/Users/kevin/code/vrusability/scripts/oculus_share_ratings.go:150 +0x398
我很困惑,因为我写了很多exactly as the documentation example demonstrates。
答案 0 :(得分:9)
您需要将指针传递给WaitGroup,而不是WaitGroup对象。当您传递实际的WaitGroup时,Go会复制该值,并在副本上调用Done()
。结果是原始WaitGroup将有十个Add并且没有Done,并且WaitGroup的每个副本将有一个Done(),并且当WaitGroup传递给函数时,有许多Add。在那里。
改为传递指针,每个函数都将引用相同的WaitGroup。
import "sync"
func doWork(wg *sync.WaitGroup) error {
defer wg.Done()
// Do some heavy lifting... request URL's or similar
return nil
}
func main() {
wg := &sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go doWork(wg)
}
}
答案 1 :(得分:0)
正如@Kevin所提到的,您需要传递对WaitGroup
的引用。这实际上是我不喜欢WaitGroup
的一件事,因为你会将并发逻辑与业务逻辑混合在一起。
所以我想出了这个通用函数来解决这个问题:
// Parallelize parallelizes the function calls
func Parallelize(functions ...func()) {
var waitGroup sync.WaitGroup
waitGroup.Add(len(functions))
defer waitGroup.Wait()
for _, function := range functions {
go func(copy func()) {
defer waitGroup.Done()
copy()
}(function)
}
}
以下是一个例子:
func1 := func() {
for char := 'a'; char < 'a' + 3; char++ {
fmt.Printf("%c ", char)
}
}
func2 := func() {
for number := 1; number < 4; number++ {
fmt.Printf("%d ", number)
}
}
Parallelize(func1, func2) // a 1 b 2 c 3
如果您想使用它,可以在https://github.com/shomali11/util
找到它