尽管在迭代之前关闭了Golang通道,但仍然会导致死锁问题。

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

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)
	}
}

我做了以下操作:

  1. 在它们执行完毕后关闭了所有的goroutine。
  2. 在所有的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 &lt;- num * num * num
		}(each)
	}
	wg.Wait()
	close(channel)

	// Print all the numbers the channel received
	for each := range channel {
		println(each)
	}
}

I am:

  1. Closing all the goroutines after they execute.
  2. 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 &lt;- 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 &lt;- 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 (
	&quot;fmt&quot;
)

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(&lt;-channel) // take out from channel
	}
}

func CubeIt(intArr []int, c chan int) {
	for i := 0; i &lt; len(intArr); i++ {
		cubed := intArr[i] * intArr[i] * intArr[i] // cube it heere
		c &lt;- cubed // put in channel
	}
}

huangapple
  • 本文由 发表于 2022年10月11日 20:40:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/74028315.html
匿名

发表评论

匿名网友

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

确定