英文:
Golang channels causing deadlocks despite closing them before iterating through them
问题
我希望有多个goroutine,每个goroutine都向一个通道中放入一个值。
在所有goroutine执行完毕后,我希望遍历通道并获取其中放入的所有值。
理想情况下,我可能应该使用一个同步集合来实现,但由于我对golang还不熟悉,我想尝试使用通道来实现。
我的程序:
func main() {
intArr := []int{3, 5, 6, 8, 2}
channel := make(chan int)
var wg sync.WaitGroup
wg.Add(len(intArr))
// 对于intArr中的每个数字,将其立方并发送到通道中
for _, each := range intArr {
go func(num int) {
defer wg.Done()
channel <- num * num * num
}(each)
}
wg.Wait()
close(channel)
// 打印通道接收到的所有数字
for each := range channel {
println(each)
}
}
我做了以下操作:
- 在它们执行完毕后关闭了所有的goroutine。
- 在所有的goroutine完成写入后关闭了通道。
但是我仍然看到我的代码出现了死锁。
执行输出:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x14000090008)
/usr/local/go/src/runtime/sema.go:56 +0x38
sync.(*WaitGroup).Wait(0x14000090000)
/usr/local/go/src/sync/waitgroup.go:130 +0x74
main.main()
/Users/varungawande/Desktop/untitled folder/test.go:21 +0xf0
goroutine 17 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x3)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
goroutine 18 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x5)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
goroutine 19 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x6)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
goroutine 20 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x8)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
goroutine 21 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x2)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
exit status 2
英文:
I wish to have multiple goroutines that put a value each into a channel.
And after all the goroutines are finished executing, I wish to iterate through the channel and get all the values which were put into them.
Ideally I should perhaps use a synchronised collection for this, but as I am new to golang, I wanted to try and implement this with channels.
My program:
func main() {
intArr := []int{3, 5, 6, 8, 2}
channel := make(chan int)
var wg sync.WaitGroup
wg.Add(len(intArr))
// For each number in intArr, cube the number and send it the channel
for _, each := range intArr {
go func(num int) {
defer wg.Done()
channel <- num * num * num
}(each)
}
wg.Wait()
close(channel)
// Print all the numbers the channel received
for each := range channel {
println(each)
}
}
I am:
- Closing all the goroutines after they execute.
- Closing the channel after all goroutines are done writing.
But still I keep seeing my code deadlock.
Execution output:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x14000090008)
/usr/local/go/src/runtime/sema.go:56 +0x38
sync.(*WaitGroup).Wait(0x14000090000)
/usr/local/go/src/sync/waitgroup.go:130 +0x74
main.main()
/Users/varungawande/Desktop/untitled folder/test.go:21 +0xf0
goroutine 17 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x3)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
goroutine 18 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x5)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
goroutine 19 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x6)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
goroutine 20 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x8)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
goroutine 21 [chan send]:
main.main.func1(0x14000090000, 0x1400008c060, 0x2)
/Users/varungawande/Desktop/untitled folder/test.go:18 +0x68
created by main.main
/Users/varungawande/Desktop/untitled folder/test.go:16 +0xd4
exit status 2
答案1
得分: 4
问题是由于通道 channel := make(chan int)
是阻塞通道,当通道已满时,其他goroutine必须等待它,导致死锁。
解决这个问题的一个简单方法是将 wg.Wait()
放在另一个goroutine中。
func main() {
intArr := []int{3, 5, 6, 8, 2}
channel := make(chan int)
var wg sync.WaitGroup
wg.Add(len(intArr))
for _, each := range intArr {
go func(num int) {
defer wg.Done()
channel <- num * num * num
}(each)
}
go func() {
wg.Wait()
close(channel)
}()
// 打印通道接收到的所有数字
for each := range channel {
println(each)
}
}
另一种解决方法是将接收通道放在另一个goroutine中。
func main() {
intArr := []int{3, 5, 6, 8, 2}
channel := make(chan int)
var wg sync.WaitGroup
wg.Add(len(intArr))
for _, each := range intArr {
go func(num int) {
defer wg.Done()
channel <- num * num * num
}(each)
}
go func() {
// 打印通道接收到的所有数字
for each := range channel {
println(each)
}
}()
wg.Wait()
close(channel)
}
英文:
The issue is that since the channel channel := make(chan int)
is blocking channel: when the channel is full, other goroutines must wait for it cause deadlock
One simple way to solve it, just put wg.Wait()
to another goroutine
func main() {
intArr := []int{3, 5, 6, 8, 2}
channel := make(chan int)
var wg sync.WaitGroup
wg.Add(len(intArr))
for _, each := range intArr {
go func(num int) {
defer wg.Done()
channel <- num * num * num
}(each)
}
go func() {
wg.Wait()
close(channel)
}()
// Print all the numbers the channel received
for each := range channel {
println(each)
}
}
https://go.dev/play/p/veddKPxFFwl
Another solution is to put the receive channel to another goroutine.
func main() {
intArr := []int{3, 5, 6, 8, 2}
channel := make(chan int)
var wg sync.WaitGroup
wg.Add(len(intArr))
for _, each := range intArr {
go func(num int) {
defer wg.Done()
channel <- num * num * num
}(each)
}
go func() {
// Print all the numbers the channel received
for each := range channel {
println(each)
}
}()
wg.Wait()
close(channel)
}
答案2
得分: 1
你需要使用长度为len(intArr)的缓冲通道进行声明:
channel := make(chan int, len(intArr))
英文:
You need to declare a buffered channel with length len(intArr)
channel := make(chan int, len(intArr))
答案3
得分: 0
看起来你已经有了一个答案,这个案例可以简化如下。
package main
import (
"fmt"
)
func main() {
intArr := []int{3, 5, 6, 8, 2}
channel := make(chan int) // 创建一个通道
go CubeIt(intArr, channel) // 创建一个Go协程
for range intArr {
fmt.Println(<-channel) // 从通道中取出数据
}
}
func CubeIt(intArr []int, c chan int) {
for i := 0; i < len(intArr); i++ {
cubed := intArr[i] * intArr[i] * intArr[i] // 在这里进行立方运算
c <- cubed // 将结果放入通道中
}
}
希望对你有帮助!
英文:
It seems you already have an annswer, this case can be simplified like this.
package main
import (
"fmt"
)
func main() {
intArr := []int{3, 5, 6, 8, 2}
channel := make(chan int) //create a channel
go CubeIt(intArr, channel) // create a go routine
for range intArr {
fmt.Println(<-channel) // take out from channel
}
}
func CubeIt(intArr []int, c chan int) {
for i := 0; i < len(intArr); i++ {
cubed := intArr[i] * intArr[i] * intArr[i] // cube it heere
c <- cubed // put in channel
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论