多个 Goroutine 上的延迟关闭?

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

Defer close on multiple Goroutines?

问题

我有以下代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	stuff := fanIn(
		generator(4, 5, 6, 7),
		generator(1, 2, 6, 3, 7),
		generator(12, 15, 33, 40, 10),
		generator(18, 13, 20, 40, 15),
		generator(100, 200, 64000, 3121, 1237),
	)

	for v := range stuff {
		fmt.Println(v)
	}

	fmt.Println(t.Sub(time.Now()))
}

func generator(nums ...int) <-chan int {
	out := make(chan int, 10)
	go func() {
		defer close(out)
		for _, v := range nums {
			out <- v
		}
	}()
	return out
}

func fanIn(in ...<-chan int) <-chan int {
	out := make(chan int, 10)

	for _, v := range in {
		go func(ch <-chan int) {
			for val := range ch {
				go func(c int) { out <- c }(val)
			}
		}(v)
	}

	return out
}

在第18行出现了死锁:

for v := range stuff {...}

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

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

谢谢!

英文:

I have the following code:

package main

import (
	&quot;fmt&quot;
    &quot;time&quot;
)

func main() {
    t := time.Now()
    stuff := fanIn(
	    generator(4, 5, 6, 7),
	    generator(1, 2, 6, 3, 7),
	    generator(12, 15, 33, 40, 10),
	    generator(18, 13, 20, 40, 15),
	    generator(100, 200, 64000, 3121, 1237),
    )

    for v := range stuff {
	    fmt.Println(v)
    }

    fmt.Println(t.Sub(time.Now()))
}

func generator(nums ...int) &lt;-chan int {
    out := make(chan int, 10)
    go func() {
	    defer close(out)
	    for _, v := range nums {
		    out &lt;- v
	    }
    }()
    return out
}

func fanIn(in ...&lt;-chan int) &lt;-chan int {
    out := make(chan int, 10)

    for _, v := range in {
	    go func(ch &lt;-chan int) {
		    for val := range ch {
			    go func(c int) { out &lt;- c }(val)
		    }
	    }(v)
    }

    return out
}

It results in a deadlock on line 18:

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来解决这个问题:

func fanIn(in ...<-chan int) <-chan int {
    // 在这里使用一个 WaitGroup
    var wg sync.WaitGroup
    out := make(chan int, 10)

    for _, v := range in {
        wg.Add(1)
        go func(ch <-chan int) {
            defer wg.Done()
            for val := range ch {
                out <- val
            }
        }(v)
    }
    
    // 在另一个 goroutine 中等待 WaitGroup 完成
    go func() {
        wg.Wait()
        close(out)
    }()
    return out
}

[可工作的代码][2]

  [1]: https://golang.org/pkg/sync/#WaitGroup
  [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:

func fanIn(in ...&lt;-chan int) &lt;-chan int {
    // use a WaitGroup here
	var wg sync.WaitGroup
	out := make(chan int, 10)

	for _, v := range in {
		wg.Add(1)
		go func(ch &lt;-chan int) {
			defer wg.Done()
			for val := range ch {
				out &lt;- val
			}
		}(v)
	}
	
    // wait for wait groups to finish in another goroutine
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

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:

确定