Pointers 我是如何混淆goroutines中变量和指针的作用域的?
我通过编写一个简单的程序来学习Go,该程序可以同时从几个http服务器下载传感器数据文件。服务器上的传感器数据文件定期刷新(30秒或2分钟,取决于“来源”)。下载数据可能需要100毫秒到10秒。因此,我阅读了每台服务器的一些配置(OriginContext)。然后我为每个OriginContext启动一个控制器。每个控制器持续触发一个goroutine,该goroutine执行下载等操作 我将我的代码精简为一个最小的示例,以某种方式/希望仍然能够显示我的意图。当我运行它时,将有两个控制器,但不知何故,当它们触发dostuff that take longtime()方法时,它们都引用相同的配置 那么,我是如何混淆goroutines中变量和指针的作用域的呢 我是一个新手,这也是我第一次尝试使用一种使用指针的语言。嗯,我害羞的C/C++尝试是十多年前的事了。。。所以我假设我的困惑在于引用/值/解引用,但我看不出来 代码如下:Pointers 我是如何混淆goroutines中变量和指针的作用域的?,pointers,go,concurrency,scope,Pointers,Go,Concurrency,Scope,我通过编写一个简单的程序来学习Go,该程序可以同时从几个http服务器下载传感器数据文件。服务器上的传感器数据文件定期刷新(30秒或2分钟,取决于“来源”)。下载数据可能需要100毫秒到10秒。因此,我阅读了每台服务器的一些配置(OriginContext)。然后我为每个OriginContext启动一个控制器。每个控制器持续触发一个goroutine,该goroutine执行下载等操作 我将我的代码精简为一个最小的示例,以某种方式/希望仍然能够显示我的意图。当我运行它时,将有两个控制器,但不知
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)
}