英文:
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 (
	"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)
	}  
    //fatal error: all goroutines are asleep - deadlock!
	fmt.Println("Done")
}
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 (
	"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)
	}
	// 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("Done")
}
答案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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论