为什么所有的goroutine都处于休眠状态 – 死锁。识别瓶颈。

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

Why all goroutines are asleep - deadlock. Identifying bottleneck

问题

  1. package main
  2. import (
  3. "fmt"
  4. "runtime"
  5. "sync"
  6. "time"
  7. )
  8. func main() {
  9. intInputChan := make(chan int, 50)
  10. var wg sync.WaitGroup
  11. for i := 0; i < 3; i++ {
  12. wg.Add(1)
  13. go worker(intInputChan, wg)
  14. }
  15. for i := 1; i < 51; i++ {
  16. fmt.Printf("输入:%d \n", i)
  17. intInputChan <- i
  18. }
  19. close(intInputChan)
  20. wg.Wait()
  21. fmt.Println("主应用程序退出...")
  22. panic("---------------")
  23. }
  24. func worker(input chan int, wg sync.WaitGroup) {
  25. defer func() {
  26. fmt.Println("执行延迟函数...")
  27. wg.Done()
  28. }()
  29. for {
  30. select {
  31. case intVal, ok := <-input:
  32. time.Sleep(100 * time.Millisecond)
  33. if !ok {
  34. input = nil
  35. return
  36. }
  37. fmt.Printf("%d %v\n", intVal, ok)
  38. default:
  39. runtime.Gosched()
  40. }
  41. }
  42. }

抛出的错误是:

致命错误:所有的goroutine都处于休眠状态 - 死锁!

goroutine 1 [semacquire]:
sync.(*WaitGroup).Wait(0xc082004600)
c:/go/src/sync/waitgroup.go:132 +0x170
main.main()
E:/Go/go_projects/go/src/Test.go:22 +0x21a

英文:
  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;runtime&quot;
  5. &quot;sync&quot;
  6. &quot;time&quot;
  7. )
  8. func main() {
  9. intInputChan := make(chan int, 50)
  10. var wg sync.WaitGroup
  11. for i := 0; i &lt; 3; i++ {
  12. wg.Add(1)
  13. go worker(intInputChan, wg)
  14. }
  15. for i := 1; i &lt; 51; i++ {
  16. fmt.Printf(&quot;Inputs. %d \n&quot;, i)
  17. intInputChan &lt;- i
  18. }
  19. close(intInputChan)
  20. wg.Wait()
  21. fmt.Println(&quot;Existing Main App... &quot;)
  22. panic(&quot;---------------&quot;)
  23. }
  24. func worker(input chan int, wg sync.WaitGroup) {
  25. defer func() {
  26. fmt.Println(&quot;Executing defer..&quot;)
  27. wg.Done()
  28. }()
  29. for {
  30. select {
  31. case intVal, ok := &lt;-input:
  32. time.Sleep(100 * time.Millisecond)
  33. if !ok {
  34. input = nil
  35. return
  36. }
  37. fmt.Printf(&quot;%d %v\n&quot;, intVal, ok)
  38. default:
  39. runtime.Gosched()
  40. }
  41. }
  42. }

error thrown is.

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.(*WaitGroup).Wait(0xc082004600)
c:/go/src/sync/waitgroup.go:132 +0x170
main.main()
E:/Go/go_projects/go/src/Test.go:22 +0x21a

答案1

得分: 8

我刚刚尝试了一下(playground),传递了一个wg *sync.WaitGroup,它可以工作。

传递sync.WaitGroup意味着传递了sync.WaitGroup的副本(按值传递):goroutine将Done()方法应用于不同的sync.WaitGroup

  1. var wg sync.WaitGroup
  2. for i := 0; i < 3; i++ {
  3. wg.Add(1)
  4. go worker(intInputChan, &wg)
  5. }

注意&wg:你通过值传递了指向原始sync.WaitGroup的指针,供goroutine使用。

英文:

I just tried it (playground) passing a wg *sync.WaitGroup and it works.

Passing sync.WaitGroup means passing a copy of the sync.WaitGroup (passing by value): the goroutine mentions Done() to a different sync.WaitGroup.

  1. var wg sync.WaitGroup
  2. for i := 0; i &lt; 3; i++ {
  3. wg.Add(1)
  4. go worker(intInputChan, &amp;wg)
  5. }

Note the &amp;wg: you are passing by value the pointer to the original sync.WaitGroup, for the goroutine to use.

答案2

得分: 3

如上所述,在sync包文档的开头附近提到,不要通过值传递来传递sync包中的类型:"不应该复制包含此包中定义的类型的值",这也包括类型本身(sync.Mutexsync.WaitGroup等)。

然而,有几点需要注意:

  • 如果你知道要添加多少个,可以只调用一次wg.Add(但要确保在任何地方调用Wait之前完成)。
  • 你不应该像那样调用runtime.Gosched,它会使工作线程忙于循环。
  • 你可以使用range从通道中读取以简化在通道关闭时停止。
  • 对于小函数,你可以使用闭包而不必传递通道或等待组。

代码如下:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. func main() {
  8. const numWorkers = 3
  9. c := make(chan int, 10)
  10. var wg sync.WaitGroup
  11. wg.Add(numWorkers)
  12. for i := 0; i < numWorkers; i++ {
  13. go func() {
  14. defer func() {
  15. fmt.Println("执行延迟操作...")
  16. wg.Done()
  17. }()
  18. for v := range c {
  19. fmt.Println("接收:", v)
  20. time.Sleep(100 * time.Millisecond)
  21. }
  22. }()
  23. }
  24. for i := 1; i < 51; i++ {
  25. fmt.Println("发送:", i)
  26. c <- i
  27. }
  28. fmt.Println("关闭通道...")
  29. close(c)
  30. fmt.Println("等待...")
  31. wg.Wait()
  32. fmt.Println("退出主应用程序...")
  33. }

点击此处查看示例

英文:

As mentioned, don't pass types from the sync package around by value, right near the top of the sync package documentation: "Values containing the types defined in this package should not be copied." That also includes the types themselves (sync.Mutex, sync.WaitGroup, etc).

However, several notes:

  • You can use just a single call to wg.Add if you know how many you're going to add (but as documented make sure it's done before anything can call Wait).
  • You don't want to call runtime.Gosched like that; it makes the workers busy loop.
  • You can use range to read from the channel to simplify stopping when it's closed.
  • For small functions you can use a closure and not bother to pass the channel or wait group at all.

That turns it into this:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;sync&quot;
  5. &quot;time&quot;
  6. )
  7. func main() {
  8. const numWorkers = 3
  9. c := make(chan int, 10)
  10. var wg sync.WaitGroup
  11. wg.Add(numWorkers)
  12. for i := 0; i &lt; numWorkers; i++ {
  13. go func() {
  14. defer func() {
  15. fmt.Println(&quot;Executing defer…&quot;)
  16. wg.Done()
  17. }()
  18. for v := range c {
  19. fmt.Println(&quot;recv:&quot;, v)
  20. time.Sleep(100 * time.Millisecond)
  21. }
  22. }()
  23. }
  24. for i := 1; i &lt; 51; i++ {
  25. fmt.Println(&quot;send:&quot;, i)
  26. c &lt;- i
  27. }
  28. fmt.Println(&quot;closing…&quot;)
  29. close(c)
  30. fmt.Println(&quot;waiting…&quot;)
  31. wg.Wait()
  32. fmt.Println(&quot;Exiting Main App... &quot;)
  33. }

<kbd>playground</kbd>

huangapple
  • 本文由 发表于 2015年2月24日 16:23:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/28691035.html
匿名

发表评论

匿名网友

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

确定