【问题标题】:Sending pointers over a channel通过通道发送指针
【发布时间】:2018-08-13 20:33:06
【问题描述】:

我正在尝试使用通道来实现一种工作池。请看下面的代码

https://play.golang.org/p/g7aKxDoP9lf(围棋游乐场)

package main

import (
    "fmt"
    "time"
)

func main() {
    q1 := make(chan int)

    fmt.Printf("worker 1\n")
    go worker1(q1)
    for i := 0; i < 10; i++ {
        fmt.Printf("sending: %v\n", i)
        q1 <- i
    }

    time.Sleep(time.Second)

    fmt.Printf("\n\nworker 2\n")
    q2 := make(chan *int)
    go worker2(q2)
    for i := 0; i < 10; i++ {
        fmt.Printf("sending: %v\n", i)
        q2 <- &i
    }
    time.Sleep(time.Second)
}

func worker1(qTodo <-chan int) {
    var curr int
    for {
        select {
        case curr = <-qTodo:
            fmt.Printf("got: %v\n", curr)
        }
    }
}

func worker2(qTodo <-chan *int) {
    var curr *int
    for {
        select {
        case curr = <-qTodo:
            fmt.Printf("got: %v\n", *curr)
        }
    }
}

这是一个示例输出

worker 1
sending: 0
got: 0
sending: 1
sending: 2
got: 1
got: 2
sending: 3
sending: 4
got: 3
got: 4
sending: 5
sending: 6
got: 5
got: 6
sending: 7
sending: 8
got: 7
got: 8
sending: 9
got: 9


worker 2
sending: 0
got: 0
sending: 1
sending: 2
got: 2
got: 2
sending: 3
sending: 4
got: 4
got: 4
sending: 5
sending: 6
got: 6
got: 6
sending: 7
sending: 8
got: 8
got: 8
sending: 9
got: 10

似乎在worker2接收到​​指针的时候,原来变量中的值已经发生了变化,这反映在打印的值中。

问题是如何避免这种情况?如何解决这个问题?

【问题讨论】:

    标签: pointers go goroutine channels


    【解决方案1】:

    接收到的指针指向的值不是您所期望的,因为您每次都向它发送指向同一个变量的指针,因此工作人员在取消引用指针时会看到该变量具有的任何值。解决此类问题的一种典型方法是在for 循环内复制变量并发送指向该变量的指针。这样,您每次都在发送指向不同对象的指针。试试这个:

    for i := 0; i < 10; i++ {
        fmt.Printf("sending: %v\n", i)
        iCopy := i
        q2 <- &iCopy
    }
    

    【讨论】:

    • 做到了。它适用于 int。就我而言,我正在发送一个指向我在循环中创建的结构的指针。所以它应该避免了这个问题,但不起作用。
    • play.golang.org/p/eXM1by-_lAF 似乎在简单的情况下工作。想知道我的用例出了什么问题。我会仔细看看的。谢谢@andy-schweig
    【解决方案2】:

    the Channels section of Effective Go 涵盖了此问题。这是一个简短的摘录,变量名称已更改以匹配您的代码:

    错误在于,在 Go for 循环中,循环变量在每次迭代中都被重用,因此 i 变量在所有 goroutine 之间共享。这不是我们想要的。我们需要确保 i 对于每个 goroutine 都是唯一的。

    接着描述了两种解决方案:

    1. i 的值作为参数传递给goroutine 中的函数
    2. 在循环中创建一个新变量并使用它来代替

    由于您的 goroutine 是在循环之外启动的,因此只有 #2 适用于您的代码。

    【讨论】:

    • 感谢@jrefior。我猜这个答案和安迪·施威格的答案差不多。
    猜你喜欢
    • 1970-01-01
    • 2022-01-01
    • 2015-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-17
    • 2013-03-09
    • 2013-02-16
    相关资源
    最近更新 更多