尝试使用’range’打印通道值后发生死锁。

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

Deadlock after attempting to print values of channel using 'range'

问题

这是我在Go Playground上的代码:

package main

import (
	"fmt"
)

func sum_up(my_int int, cs chan int) {
	my_sum := 0
	for i := 0; i < my_int; i++ {
		my_sum += i
	}
	cs <- my_sum
}

func main() {

	my_channel := make(chan int)
	for i := 2; i < 5; i++ {
		go sum_up(i, my_channel)
	}

	for ele := range my_channel {
		fmt.Println(ele)
	}

	fmt.Println("Done")

}

运行结果为:

1
3
6
fatal error: all goroutines are asleep - deadlock!

我不明白是什么导致了这个错误。我理解的是,在我的函数sum_up中,我正在向my_channel添加新的值。为什么在尝试打印这些值之后出现问题?因为我看到1、3、6被打印出来,这意味着所有的goroutine都已经成功完成。

此外,如果移除尝试打印通道值的代码块:

for ele := range my_channel {
	fmt.Println(ele)
}

那么我就不会得到错误。所以问题出在导致错误的代码块中,但是为什么呢?

英文:

Here is my code at Go Playground

package main

import (
	&quot;fmt&quot;
)

func sum_up(my_int int, cs chan int) {
	my_sum := 0
	for i := 0; i &lt; my_int; i++ {
		my_sum += i
	}
	cs &lt;- my_sum
}

func main() {

	my_channel := make(chan int)
	for i := 2; i &lt; 5; i++ { 
		go sum_up(i, my_channel)
	}
	
	
	for ele := range my_channel {
		fmt.Println(ele)
	}  

    //fatal error: all goroutines are asleep - deadlock!

	fmt.Println(&quot;Done&quot;)

}

Which results in:

1
3
6
fatal error: all goroutines are asleep - deadlock!

And I don't understand what causes the error. My understanding is that in my function sum_up I am adding new values to my_channel. Why does the problem occur after I try to print out the values? Since I see 1,3,6 are printed it means that all goroutines have successfully finished.

Moreover, if the block that attempts to print the values of the channel

	for ele := range my_channel {
		fmt.Println(ele)
	}

Is removed, then I don't get the error. So it is including the block that causes the error, but why?

答案1

得分: 3

一个空通道上的for-range循环将会阻塞,直到从通道中读取到元素或者通道被关闭。

这里是一个使用sync.WaitGroup的版本,用于记录还有多少个goroutine处于活动状态。当所有的goroutine都完成后,通道会被关闭,for-range循环退出。

package main

import (
	"fmt"
	"sync"
)

func sum_up(my_int int, cs chan int, wg *sync.WaitGroup) {
	my_sum := 0
	for i := 0; i < my_int; i++ {
		my_sum += i
	}
	cs <- my_sum
	wg.Done()
}

func main() {
	wg := &sync.WaitGroup{}
	my_channel := make(chan int)
	for i := 2; i < 5; i++ {
		wg.Add(1)
		go sum_up(i, my_channel, wg)
	}

	// 运行一个goroutine来监视有多少个sum_up正在运行。
	go func(cs chan int, wg *sync.WaitGroup) {
		wg.Wait()
		close(cs)
	}(my_channel, wg)

	for ele := range my_channel {
		fmt.Println(ele)
	}

	// fatal error: all goroutines are asleep - deadlock!

	fmt.Println("Done")

}

链接:https://play.golang.org/p/ZnLYxLMNdF

英文:

A for-range on a empty channel will block until there are elements to read from the channel or until the channel is closed.

Here is a version that uses sync.WaitGroup to account for how many goroutines remain active. After all goroutines have finished, the channel is closed and the for-range loop exists.

https://play.golang.org/p/ZnLYxLMNdF

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

func sum_up(my_int int, cs chan int, wg *sync.WaitGroup) {
	my_sum := 0
	for i := 0; i &lt; my_int; i++ {
		my_sum += i
	}
	cs &lt;- my_sum
	wg.Done()
}

func main() {
	wg := &amp;sync.WaitGroup{}
	my_channel := make(chan int)
	for i := 2; i &lt; 5; i++ {
		wg.Add(1)
		go sum_up(i, my_channel, wg)
	}

	// Run a goroutine that will monitor how many sum_up are running.
	go func(cs chan int, wg *sync.WaitGroup) {
		wg.Wait()
		close(cs)
	}(my_channel, wg)

	for ele := range my_channel {
		fmt.Println(ele)
	}

	//fatal error: all goroutines are asleep - deadlock!

	fmt.Println(&quot;Done&quot;)

}

答案2

得分: 2

当你在通道上使用range时,它会无限期地等待值,直到通道关闭。这会导致死锁,因为当最后一个值被写入my_channel时,它将永远等待一个永远不会到来的值。

这里有一个稍微修改过的变体,展示了如何干净地退出range循环:https://play.golang.org/p/YDlM8EcRnx

英文:

When you use range on a channel, it will wait for values forever or until the channel is closed. It's deadlocking because when the last value is written to my_channel, it will wait forever for a value that will never come.

Here's a slightly modified variant that shows how to cleanly leave the range: https://play.golang.org/p/YDlM8EcRnx

答案3

得分: 0

for range chan 在接收到关闭信号时退出。你必须在某个地方使用 close(my_channel),否则循环将一直等待。

英文:

for range chan exit when chan receive close signal. You must close(my_channel) somewhere, or loop will wait forever.

huangapple
  • 本文由 发表于 2015年1月26日 03:25:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/28140496.html
匿名

发表评论

匿名网友

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

确定