我试图在使用频道时实施here。只是在屏幕上打印切片时,下面的代码工作正常,但是当使用通道将数组传递到主函数上的for / range循环时,会发生一些意外的行为,并且切片/数组以双面打印而不是所有排列都是发送。我想也许我之前关闭频道的主要功能是能够打印结果,但我不会期望双重打印。为什么会发生这种情况以及如何使其发挥作用。
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)
}
}
答案 0 :(得分:2)
您的问题是切片是引用类型,并且正在多个goroutine中访问。在perm
,您在每个步骤完成处理后直接打印a
。在permch
中,您通过频道发送a
,然后立即开始再次修改。由于通过通道发送的每个切片引用相同的底层数组,因此您的下一个循环迭代是否会改变a
或者您的Println()
调用是否首先获取到该数组的竞争条件。
一般情况下,如果您在使用goroutine的任何程序中遇到意外行为,则可能有竞争条件。使用-race
标志运行程序以查看位置。
编辑,关闭频道也不会影响该频道的常规阅读。可以继续读取通道,直到其缓冲区为空,此时它将开始返回该类型的零值。通道上的范围循环只有在通道关闭后才会终止并且其缓冲区为空。
答案 1 :(得分:0)
看起来permch
正在修改a
main
正在打印它,因此您的输出会出现乱码。
我可以想到三个简单的解决方法:
a
的访问权限。a
的副本:permch
可以继续。 (不要真的推荐这个,但它确实有用)。2号非常简单:
a2 := make([]int64, len(a))
copy(a2,a)
ch <- a2
是我的推荐。
对于#1,只需将var m sync.Mutex
声明为包变量,Lock
即可随时阅读或修改a
。正如你所指出的那样,这是读者和下一次修改之间的竞争条件,所以它可能毕竟不是一个好的解决方案。