通道和等待组进入死锁

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

Channels and Wait Groups Entering Deadlock

问题

我遇到了一些问题,无法控制goroutine并使它们返回到主goroutine上的通道。为了简化问题,我的代码大致如下:

func main() {
    channel := make(chan string)
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go performTest(channel, &wg, i)
    }

    wg.Wait()
    close(channel)

    for line := range channel {
        fmt.Print(line)
    }
}

func performTest(channel chan string, wg *sync.WaitGroup, i int) {
    defer wg.Done()
    // 在这里执行一些工作
    result := fmt.Sprintf("假装结果 %d", i)
    channel <- result
}

这段代码似乎陷入了某种死锁状态,但我不明白为什么。它在wg.Wait()处被阻塞,尽管我期望它在所有goroutine调用了Done之后继续执行。我在这里漏掉了什么?我想等待goroutine完成,然后迭代通道中的所有结果。

英文:

I'm having trouble wrangling go routines and getting them to communicate back to a channel on the main go routine. To simplify, my code looks something like this:


func main() {
    channel := make(chan string)
    var wg sync.WaitGroup
    for i := 0; i &lt; 10; i++ {
        wg.Add(1)
        go performTest(channel, &amp;wg, i)
    }

	wg.Wait()
	close(channel)

	for line := range channel {
		fmt.Print(line)
	}
}

func performTest(channel chan string, wg *sync.WaitGroup, i int) {
     defer wg.Done()
     // perform some work here
     result := fmt.sprintf(&quot;Pretend result %d&quot;, i)
     channel &lt;- result
}

This seems to enter into some kind of a deadlock, but I don't understand why. It gets stuck on wg.Wait(), even though I would expect it to continue once all the goroutines have called Done on the wait group. What am I missing here? I'd like to wait for the goroutines, and then iterate over all results in the channel.

答案1

得分: 2

你可以等待组并在单独的Go协程中关闭通道。如果通道关闭,那么在接收到最后一个发送的值之后,你的通道遍历将结束。

如果你只是等待,通道将不会接收任何内容。由于通道是无缓冲的,performTest 协程将无法发送。对于无缓冲的通道,发送操作将阻塞,直到接收方接收到该值。因此,延迟的 wg.Done 调用永远不会发生,你的程序将发生死锁。因为 Done 只有在永久阻塞的发送操作执行后才会被调用。

func main() {
	channel := make(chan string)
	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go performTest(channel, &wg, i)
	}
    
    // 这是一个技巧
	go func() {
		wg.Wait()
		close(channel)
	}()

	for line := range channel {
		fmt.Print(line)
	}
}

func performTest(channel chan string, wg *sync.WaitGroup, i int) {
	defer wg.Done()
	// 在这里执行一些工作
	result := fmt.Sprintf("假装的结果 %d\n", i)
	channel <- result
}

https://play.golang.com/p/5pACJzwL4Hi

英文:

You can wait for the group and close the channel in a separate go routine. If the channel is closed, your range over the channel will end after the last sent value has been received.

If you just wait, nothing will receive from the channel. Since the channel is unbuffered, the performTest goroutines won't be able to send. For an unbuffered channel, the send operation will block until it has been received. Therefore, the deferred wg.Done call would never happen, and your program is deadlocked. Since Done is only called after the forever-blocking send has been performed.

func main() {
	channel := make(chan string)
	var wg sync.WaitGroup
	for i := 0; i &lt; 10; i++ {
		wg.Add(1)
		go performTest(channel, &amp;wg, i)
	}
    
    // this is the trick
	go func() {
		wg.Wait()
		close(channel)
	}()

	for line := range channel {
		fmt.Print(line)
	}
}

func performTest(channel chan string, wg *sync.WaitGroup, i int) {
	defer wg.Done()
	// perform some work here
	result := fmt.Sprintf(&quot;Pretend result %d\n&quot;, i)
	channel &lt;- result
}

https://play.golang.com/p/5pACJzwL4Hi

huangapple
  • 本文由 发表于 2022年1月27日 17:49:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/70876451.html
匿名

发表评论

匿名网友

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

确定