Getting "fatal error: all goroutines are asleep – deadlock!" when using sync.WaitGroup

huangapple go评论101阅读模式
英文:

Getting "fatal error: all goroutines are asleep - deadlock!" when using sync.WaitGroup

问题

我正在尝试启动一组 goroutine,并等待它们全部完成。

import "sync"

func doWork(wg sync.WaitGroup) error {
    defer wg.Done()
    // 进行一些繁重的工作... 请求 URL 或类似的操作
    return nil
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go doWork(wg)
    }
    wg.Wait()
}

然而,当我运行这段代码时,我得到以下错误:

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

我感到困惑,因为我几乎完全按照文档示例的方式编写了代码。

英文:

I'm trying to spin off a set of goroutines, and then wait for them all to finish.

import &quot;sync&quot;

func doWork(wg sync.WaitGroup) error {
    defer wg.Done()
    // Do some heavy lifting... request URL&#39;s or similar
    return nil
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i &lt; 10; i++ {
        wg.Add(1)
        go doWork(wg)
    }
    wg.Wait()
}

However when I run this code I get the following error:

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

I'm confused because I wrote it pretty much exactly as the documentation example demonstrates.

答案1

得分: 14

你需要传递一个指向WaitGroup的指针,而不是WaitGroup对象本身。当你传递实际的WaitGroup时,Go语言会复制该值,并在副本上调用Done()方法。结果是原始的WaitGroup将有十个Add操作而没有Done操作,而每个WaitGroup的副本将有一个Done操作和在将WaitGroup传递给函数时存在的Add操作数量。

改为传递指针,这样每个函数都将引用同一个WaitGroup。

import "sync"

func doWork(wg *sync.WaitGroup) error {
    defer wg.Done()
    // 进行一些繁重的工作... 请求URL或类似操作
    return nil
}

func main() {
    wg := &sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go doWork(wg)
    }
}
英文:

You need to pass a pointer to the WaitGroup, and not the WaitGroup object. When you pass the actual WaitGroup, Go makes a copy of the value, and calls Done() on the copy. The result is the original WaitGroup will have ten Add's and no Done's, and each copy of the WaitGroup will have one Done() and however many Add's were there when the WaitGroup was passed to the function.

Pass a pointer instead, and every function will reference the same WaitGroup.

import &quot;sync&quot;

func doWork(wg *sync.WaitGroup) error {
    defer wg.Done()
    // Do some heavy lifting... request URL&#39;s or similar
    return nil
}

func main() {
    wg := &amp;sync.WaitGroup{}
    for i := 0; i &lt; 10; i++ {
        wg.Add(1)
        go doWork(wg)
    }
}

答案2

得分: 1

正如@Kevin提到的,您需要传递一个对WaitGroup的引用。这实际上是我不喜欢WaitGroup的一点,因为您将会将并发逻辑与业务逻辑混合在一起。

所以我想出了这个通用函数来解决这个问题:

// Parallelize并行执行函数调用
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

英文:

As @Kevin mentioned, you will need to pass a reference to your WaitGroup. This is actually the one thing I do not like about WaitGroup because you would be mixing your concurrency logic with your business logic.

So I came up with this generic function to solve this problem for me:

// 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)
    }
}

Here is an example:

func1 := func() {
        for char := &#39;a&#39;; char &lt; &#39;a&#39; + 3; char++ {
            fmt.Printf(&quot;%c &quot;, char)
        }
}

func2 := func() {
        for number := 1; number &lt; 4; number++ {
            fmt.Printf(&quot;%d &quot;, number)
        }
}

Parallelize(func1, func2)  // a 1 b 2 c 3

If you would like to use it, you can find it here https://github.com/shomali11/util

huangapple
  • 本文由 发表于 2014年8月11日 09:59:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/25234898.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定