【问题标题】:golang implementing generator / yield with channels: odd channel behaviorgolang 使用通道实现生成器/收益:奇怪的通道行为
【发布时间】:2016-06-06 01:01:50
【问题描述】:

以下代码在 golang 中实现了 yield 模式。作为一个实验,我正在实现一个all permutations 生成器。但是,当我将切片 A 返回到通道时,如果我不创建数组的新副本,则会得到不正确的结果。

请查看“???”周围的代码。有人可以解释这里的幕后发生了什么吗?我认为由于通道没有缓冲,我保证在将数组的切片发布到通道后,我确保在继续之前会消耗结果。

package main

import (
    "fmt"
)

func swap(A []int, i int, j int) {
    t := A[i]
    A[i] = A[j]
    A[j] = t
}

func recurse(A []int, c chan []int, depth int) {

    if depth == len(A) {
        // ??? Why do I need to copy the data?
        // If I do c <- A I get an incorrect answer.
        ra := make([]int, len(A))
        copy(ra, A)
        c <- ra
        return
    }

    for i := depth; i < len(A); i++ {
        swap(A, depth, i)
        recurse(A, c, depth+1)
        swap(A, depth, i)
    }
}

func yieldPermutations(A []int, c chan []int) {
    recurse(A, c, 0)
    close(c)
}

func main() {
    A := []int{1, 2, 3}
    c2 := make(chan []int)

    go yieldPermutations(A, c2)
    for v := range c2 {
        fmt.Println(v)
    }
}

如果我不复制数据,我会得到以下结果:

[1 3 2]
[1 3 2]
[2 3 1]
[2 3 1]
[3 1 2]
[3 1 2]

显然,正确的结果(我们通过数据复制得到)是:

[1 2 3]
[1 3 2]
[2 1 3]
[2 3 1]
[3 2 1]
[3 1 2]

【问题讨论】:

  • A []int 不是一个数组,它是一个切片。
  • 使用闭包来模拟生成器可能会更好。
  • 我最初做了一个闭包,但生成的代码非常复杂——我不愿意在生产中使用。
  • @Volker 已更正,谢谢。

标签: go


【解决方案1】:

认为这段代码就像 Python 中的生成器/yield 是错误的,这就是导致错误的原因。

在 Python 中,当您从生成器请求下一项时,生成器开始执行并在到达下一个 yield &lt;value&gt; 语句时停止。 Python 的生成器没有并行性:消费者运行直到它想要一个值,然后生成器运行直到它产生一个值,然后消费者获取值并继续执行。

在您的 go 代码中,goroutine 与消费项目的代码同时执行。一旦从主代码的通道中读取了一个项目,goroutine 就会同时工作以生成下一个项目。 goroutine 和 consumer 都运行直到它们到达通道发送/接收,然后值从 goroutine 发送到 consumer,然后它们都继续执行。

这意味着A 的后备数组在 goroutine 生成下一项时同时被修改。这是一种竞争条件,会导致您的意外输出。为了证明这是一场比赛,请在频道发送后插入time.Sleep(time.Second)。然后代码会产生正确的结果(虽然速度很慢):https://play.golang.org/p/uEa_k6Brcc

【讨论】:

  • 完全有道理。所以这意味着基本上,如果一个人真的想推动它工作,两个交替数组会正常工作,对吧?
  • 我明白你关于两个交替数组的意思,但我不明白它在实践中是如何工作的,因为你的排列代码保持在 A 中的状态。原则上它可以避免竞争条件.
猜你喜欢
  • 2017-09-29
  • 1970-01-01
  • 2017-02-14
  • 2013-12-11
  • 2021-11-05
  • 1970-01-01
  • 1970-01-01
  • 2021-06-23
  • 2019-01-30
相关资源
最近更新 更多