多个 Goroutine 上的延迟关闭?

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

Defer close on multiple Goroutines?

问题

我有以下代码:

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func main() {
  7. t := time.Now()
  8. stuff := fanIn(
  9. generator(4, 5, 6, 7),
  10. generator(1, 2, 6, 3, 7),
  11. generator(12, 15, 33, 40, 10),
  12. generator(18, 13, 20, 40, 15),
  13. generator(100, 200, 64000, 3121, 1237),
  14. )
  15. for v := range stuff {
  16. fmt.Println(v)
  17. }
  18. fmt.Println(t.Sub(time.Now()))
  19. }
  20. func generator(nums ...int) <-chan int {
  21. out := make(chan int, 10)
  22. go func() {
  23. defer close(out)
  24. for _, v := range nums {
  25. out <- v
  26. }
  27. }()
  28. return out
  29. }
  30. func fanIn(in ...<-chan int) <-chan int {
  31. out := make(chan int, 10)
  32. for _, v := range in {
  33. go func(ch <-chan int) {
  34. for val := range ch {
  35. go func(c int) { out <- c }(val)
  36. }
  37. }(v)
  38. }
  39. return out
  40. }

在第18行出现了死锁:

  1. for v := range stuff {...}

问题(我认为)是我没有在返回只读通道的fanIn函数上延迟关闭。我不知道何时延迟关闭,因为它必须等待多个goroutine完成。

解决这个死锁的惯用方法是什么?这段代码是否符合惯用方式?

谢谢!

英文:

I have the following code:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;time&quot;
  5. )
  6. func main() {
  7. t := time.Now()
  8. stuff := fanIn(
  9. generator(4, 5, 6, 7),
  10. generator(1, 2, 6, 3, 7),
  11. generator(12, 15, 33, 40, 10),
  12. generator(18, 13, 20, 40, 15),
  13. generator(100, 200, 64000, 3121, 1237),
  14. )
  15. for v := range stuff {
  16. fmt.Println(v)
  17. }
  18. fmt.Println(t.Sub(time.Now()))
  19. }
  20. func generator(nums ...int) &lt;-chan int {
  21. out := make(chan int, 10)
  22. go func() {
  23. defer close(out)
  24. for _, v := range nums {
  25. out &lt;- v
  26. }
  27. }()
  28. return out
  29. }
  30. func fanIn(in ...&lt;-chan int) &lt;-chan int {
  31. out := make(chan int, 10)
  32. for _, v := range in {
  33. go func(ch &lt;-chan int) {
  34. for val := range ch {
  35. go func(c int) { out &lt;- c }(val)
  36. }
  37. }(v)
  38. }
  39. return out
  40. }

It results in a deadlock on line 18:

  1. for v := range stuff {...}

The issue (I think) is that I'm not deferring the close on the fanIn function that returns a read-only channel. I don't know when to defer it since it's got to wait for the end of multiple goroutines to complete.

What's the idiomatic way to solve this deadlock? Is this code even idiomatic?

Thanks!

GoPlay

答案1

得分: 4

你对错误原因的判断是正确的,即未关闭fanIn的通道。你可以使用sync.WaitGroup来解决这个问题:

  1. func fanIn(in ...<-chan int) <-chan int {
  2. // 在这里使用一个 WaitGroup
  3. var wg sync.WaitGroup
  4. out := make(chan int, 10)
  5. for _, v := range in {
  6. wg.Add(1)
  7. go func(ch <-chan int) {
  8. defer wg.Done()
  9. for val := range ch {
  10. out <- val
  11. }
  12. }(v)
  13. }
  14. // 在另一个 goroutine 中等待 WaitGroup 完成
  15. go func() {
  16. wg.Wait()
  17. close(out)
  18. }()
  19. return out
  20. }
  21. [可工作的代码][2]
  22. [1]: https://golang.org/pkg/sync/#WaitGroup
  23. [2]: https://play.golang.org/p/sWxVay-KWP

请注意,我只翻译了代码部分,其他内容不包括在内。

英文:

You are correct about the cause of error being un-closed fanIn's channel. You can use a sync.WaitGroup to resolve the issue:

  1. func fanIn(in ...&lt;-chan int) &lt;-chan int {
  2. // use a WaitGroup here
  3. var wg sync.WaitGroup
  4. out := make(chan int, 10)
  5. for _, v := range in {
  6. wg.Add(1)
  7. go func(ch &lt;-chan int) {
  8. defer wg.Done()
  9. for val := range ch {
  10. out &lt;- val
  11. }
  12. }(v)
  13. }
  14. // wait for wait groups to finish in another goroutine
  15. go func() {
  16. wg.Wait()
  17. close(out)
  18. }()
  19. return out
  20. }

Working code.

huangapple
  • 本文由 发表于 2016年8月19日 12:41:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/39031248.html
匿名

发表评论

匿名网友

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

确定