我试图实现 Heap's Algorithm在使用 channel 。当仅在屏幕上打印 slice 时,下面的代码工作正常,但是当使用 channel 将数组传递到主函数上的 for/range 循环时,会发生一些意外行为,并且 slice/数组以重复的方式打印,并且并非所有排列都被打印。发送。我想也许我会在主函数能够打印结果之前关闭 channel ,但我不希望出现双重打印。为什么会发生这种情况以及我怎样才能让它发挥作用。
package main
import "fmt"
func perm(a []int64) {
var n = len(a)
var c = make([]int, n)
fmt.Println(a)
i := 0
for i < n {
if c[i] < i {
if i%2 == 0 {
a[0], a[i] = a[i], a[0]
} else {
a[c[i]], a[i] = a[i], a[c[i]]
}
fmt.Println(a)
c[i]++
i = 0
} else {
c[i] = 0
i++
}
}
}
func permch(a []int64, ch chan<- []int64) {
var n = len(a)
var c = make([]int, n)
ch <- a
i := 0
for i < n {
if c[i] < i {
if i%2 == 0 {
a[0], a[i] = a[i], a[0]
} else {
a[c[i]], a[i] = a[i], a[c[i]]
}
ch <- a
c[i]++
i = 0
} else {
c[i] = 0
i++
}
}
close(ch)
}
func main() {
var i = []int64{1, 2, 3}
fmt.Println("Without Channels")
perm(i)
ch := make(chan []int64)
go permch(i, ch)
fmt.Println("With Channels")
for slc := range ch {
fmt.Println(slc)
}
}
最佳答案
您的问题是 slice 是引用类型,并且正在多个 goroutine 中访问。在 perm
中,您在每一步完成处理后直接打印 a
。在 permch
中,您通过 channel 发送 a
,但随后立即开始再次修改它。由于通过 channel 发送的每个 slice 都引用相同的底层数组,因此您会遇到竞争条件,即下一次循环迭代是否会更改 main 中的 a
或 Println()
调用首先到达该数组。
一般来说,如果您在任何使用 goroutine 的程序中遇到意外行为,则可能存在竞争条件。使用 -race
标志运行程序以查看位置。
编辑:此外,关闭 channel 不会影响 channel 的例行读取操作。可以继续读取 channel ,直到其缓冲区为空,此时它将开始返回该类型的零值。仅当 channel 关闭并且其缓冲区为空时, channel 上的范围循环才会终止。
关于实现堆排列算法时,Golang 范围内的 channel 具有奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43722558/