Pointers 我是如何混淆goroutines中变量和指针的作用域的?

Pointers 我是如何混淆goroutines中变量和指针的作用域的?,pointers,go,concurrency,scope,Pointers,Go,Concurrency,Scope,我通过编写一个简单的程序来学习Go,该程序可以同时从几个http服务器下载传感器数据文件。服务器上的传感器数据文件定期刷新(30秒或2分钟,取决于“来源”)。下载数据可能需要100毫秒到10秒。因此,我阅读了每台服务器的一些配置(OriginContext)。然后我为每个OriginContext启动一个控制器。每个控制器持续触发一个goroutine,该goroutine执行下载等操作 我将我的代码精简为一个最小的示例,以某种方式/希望仍然能够显示我的意图。当我运行它时,将有两个控制器,但不知

我通过编写一个简单的程序来学习Go,该程序可以同时从几个http服务器下载传感器数据文件。服务器上的传感器数据文件定期刷新(30秒或2分钟,取决于“来源”)。下载数据可能需要100毫秒到10秒。因此,我阅读了每台服务器的一些配置(OriginContext)。然后我为每个OriginContext启动一个控制器。每个控制器持续触发一个goroutine,该goroutine执行下载等操作

我将我的代码精简为一个最小的示例,以某种方式/希望仍然能够显示我的意图。当我运行它时,将有两个控制器,但不知何故,当它们触发dostuff that take longtime()方法时,它们都引用相同的配置

那么,我是如何混淆goroutines中变量和指针的作用域的呢

我是一个新手,这也是我第一次尝试使用一种使用指针的语言。嗯,我害羞的C/C++尝试是十多年前的事了。。。所以我假设我的困惑在于引用/值/解引用,但我看不出来

代码如下:

package main

import (
        "log"
        "time"
)

type OriginContext struct {
        Origin   string
        Offset   time.Duration
        Interval time.Duration
}

type Controller struct {
        originContext *OriginContext
}

func NewController(originContext *OriginContext) (w *Controller) {
        log.Printf("Controller starting loop for origin %s.", originContext.Origin)
        w = &Controller{originContext}
        w.start()
        return w
}

func (w *Controller) start() {
        log.Println("start() of", w.originContext.Origin)
        go func() {
                time.Sleep(w.originContext.Offset)
                ticker := time.NewTicker(w.originContext.Interval)
                go w.doStuffThatMayTakeLongTime() // iteration zero
                for {
                        select {
                        case <-ticker.C:
                                go w.doStuffThatMayTakeLongTime()
                        }
                }
        }()
}

func (w *Controller) doStuffThatMayTakeLongTime() {
        log.Printf("%s doing stuff", w.originContext.Origin)
}

func main() {
        contexts := []OriginContext{
                {
                        Origin:   "alpha",
                        Offset:   0 * time.Second,
                        Interval: 5 * time.Second,
                },
                {
                        Origin:   "bravo",
                        Offset:   5 * time.Second,
                        Interval: 10 * time.Second,
                },
        }
        for _, ctx := range contexts {
                log.Printf("Starting Controller %s.", ctx.Origin)
                _ = NewController(&ctx)
        }
        select {}
}

应该有阿尔法和布拉沃在做事情,但只有布拉沃

问题出在以下几行:

    for _, ctx := range contexts {
            log.Printf("Starting Controller %s.", ctx.Origin)
            _ = NewController(&ctx)
    }
变量
ctx
在循环的每次迭代中都被重用
NewController
是在循环的每次迭代中传递给这个变量的地址。程序将打印存储在此变量中的最后一个值(尽管这不能保证,但变量上存在竞争)

有几种方法可以解决这个问题。一种方法是将代码更改为:

for i := range contexts {
        log.Printf("Starting Controller %s.", context[i].Origin)
        _ = NewController(&context[i])
}

通过此更改,NewController被传递到slice元素的指针,而不是函数中变量的指针

另一个选项是在循环体中声明一个新变量:

    for _, ctx := range contexts {
            ctx := ctx // <-- add this line
            log.Printf("Starting Controller %s.", ctx.Origin)
            _ = NewController(&ctx)
    }
for ux,ctx:=范围上下文{

ctx:=ctx//尝试记录:
log.Printf(“控制器从地址%v的上下文开始,originContext)”
NewController
函数的开头。您应该看到两次相同的地址。
    for _, ctx := range contexts {
            ctx := ctx // <-- add this line
            log.Printf("Starting Controller %s.", ctx.Origin)
            _ = NewController(&ctx)
    }