在频道上发送指针

问题描述 投票:3回答:2

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

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

似乎在工人2接收指针时,该值已经在原始变量中改变,该原始变量反映在打印的值中。

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

pointers go goroutine channels
2个回答
4
投票

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

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

4
投票

这个问题在the Channels section of Effective Go中有所涉及。这是一个简短的摘录,变量名称已更改为与您的代码匹配:

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

它继续描述两种解决方案:

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

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

© www.soinside.com 2019 - 2024. All rights reserved.