在实现堆时,Golang通过通道的范围具有奇怪的行为';s置换算法

在实现堆时,Golang通过通道的范围具有奇怪的行为';s置换算法,go,range,permutation,slice,channels,Go,Range,Permutation,Slice,Channels,我尝试使用通道在围棋中实现。当仅在屏幕上打印切片时,下面的代码工作正常,但当使用通道将数组传递到主函数上的for/range循环时,会出现一些意外行为,切片/数组以双面打印,并且不会发送所有排列。我想我可能会在主函数能够打印结果之前关闭频道,但我不希望出现双重打印。为什么会发生这种情况,我如何才能让它工作 package main import "fmt" func perm(a []int64) { var n = len(a) var c = make([]int, n)

我尝试使用通道在围棋中实现。当仅在屏幕上打印切片时,下面的代码工作正常,但当使用通道将数组传递到主函数上的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)
    }

}
主程序包
输入“fmt”
func perm(a[]int64){
var n=len(a)
var c=制造([]整数,n)
fmt.Println(a)
i:=0
对于ifunc permch(a[]int64,ch chan您的问题是切片是引用类型,并且在多个goroutine中被访问。在
perm
中,您在每个步骤完成处理时直接打印
a
。在
permch
中,您通过通道发送
a
,但随后立即开始再次修改它。因为eac通过通道发送的h slice引用相同的底层数组,对于下一次循环迭代是改变
a
,还是先调用
Println()

通常,如果您在使用goroutines的任何程序中遇到意外行为,则可能存在争用情况。请使用
-race
标志运行该程序以查看位置


编辑:此外,关闭通道对通道的例行读取没有影响。可以继续读取通道,直到其缓冲区为空,此时它将开始返回该类型的零值。通道上的范围循环仅在通道关闭且缓冲区为空时终止。

permch
在打印
main
的同时修改
a
,因此您的输出是乱码

我可以想出三个简单的解决办法:

  • 使用互斥锁保护对
    a
    的访问
  • 在频道上放置一份
    a
  • 从它已经打印的main中获得某种返回信号,并且
    permch
    可以继续(不建议这样做,但它可以工作)
  • 第二点很简单:

    a2 := make([]int64, len(a))
    copy(a2,a)
    ch <- a2
    
    a2:=make([]int64,len(a))
    副本(a2,a)
    
    你能告诉我如何实现#1或#2吗?我认为在从main到goroutine的信号中添加另一个通道有点麻烦。我尝试过使用内置的
    copy()
    make()
    创建一个新的底层数组,但我认为问题在于主打印数组的同时
    permch
    会改变它。我真的不知道如何使用互斥体,因为我可以锁定permch中正在改变数组的部分,但不能保证它会等待
    main()
    打印数组。当我尝试使用互斥时,我可以使竞争检测器静音,但无法得到我想要的,这是无通道版本的行为。