GoRoutines并将struct传递给原始上下文

时间:2018-03-13 23:01:36

标签: go struct goroutine

我有一个配置,它定义了一些实例(SomeConfigItems),它们为每个实例创建了一个thing()。

该东西是包含的包返回的结构,其中包含Price(float64)和嵌套结构。嵌套的结构维护着一个交易图。

问题是我能够遍历thing.Streams.Trades并查看我的main()为{}循环实时发生的所有交易。我无法看到更新的东西。即使它偶尔设置在Handler中也是如此。

我很难理解嵌套结构如何包含数据但不包含Price。我觉得好像缺少了一些范围,goroutine或者可能用于实例化新对象的指针。

任何帮助将不胜感激,我会在此期间继续阅读。我已将代码缩减为相似的内容。

main.go:

package main
import thing
var Things []thing.Handler

for _, name := range SomeConfigItems {
  handler := thing.New(name)
  Things = append(Things, handler)
}

for {
  for _, t := range Things {
    log.Info("Price: ", t.Price) // This is set to 0 every iteration, but I can actively data in thing.Streams.Trades
  }
}

thing.go:

package thing
import streams

type Handler struct {
    Name  string
    Price   float64
    Streams streams.Streams
}

func New(name string) (h Handler, err error) {
    stream, err := streams.New(strings.ToLower(name))
    h = Handler{
        Name:  name,
        Price: "0.0"
        Streams: stream,
    }
  go h.handler()
    return h, err
}

func (bot *Handler) handler() {
    var currentPrice float64
    for {
        currentPrice = external.GetPrice(bot.Name).Price // Validated that this returns a float64
        bot.Price = currentPrice                           // Verified that this is updated immediately after in this context.
                                                       // Unable to see Price updated from outer context. 
    }
}

streams.go:

package streams

type Streams struct {
    Trades
}

type State struct {
  Price         string `json:"p"`
  Quantity      string `json:"q"`
}

type Trades struct {
    Trades     map[float64]float64
    TradeMutex sync.Mutex
    Updates    chan State
}

func New(name string) (s Streams, err error) {
    p := newTradeStream(name)
    s = Streams{
        Trades: p,
    }
    return s, err
}

func newTradeStream(name string) (ts Trades) {
    ts = Trades{}
    ts.Trades = make(map[float64]float64, MaxDepth)
    ts.Updates = make(chan State, 500)
  // ... Other watchdog code
    return ts
}

注意:

我在多个位置添加了一些调试日志记录。在Bot Handler中,价格被打印(成功),然后更新,然后再次打印(成功) - 在handler()函数中显示Price的设置没有间隙。

当为{}循环的main()添加相同类型的调试时,我尝试设置递增计数器并分配thing.Price的值 - 打印thing.Price在每个循环上导致0,即使我在同一个循环中设置价格(并验证它是否已设置),在下一次迭代时返回0。

这种行为就是为什么我认为我错过了一些非常基本的东西。

1 个答案:

答案 0 :(得分:1)

在Go中,参数按值传递给函数 - 意味着函数获取的是值的副本,而不是对变量的引用。功能接收器和返回列表也是如此。

这不是最优雅的描述,但为了便于解释,我们称之为“功能墙”。如果以某种方式传递的值是指针,则该函数仍然获得一个副本,但它是内存地址的副本,因此指针可用于更改另一侧的变量值。壁。如果它是一个引用类型,它在类型的实现中使用指针,那么再次指向的东西的更改可以穿过该墙。但是否则这种变化不会跨越墙壁,这就是为什么这么多Go函数被写入返回值而不是仅修改值的一个原因。

这是一个可运行的例子:

package main

import (
    "fmt"
)

type Car struct {
    Color   string
}

func (c Car) Change() {  // c was passed by value, it's a copy
    c.Color = "Red"
}

func main() {
    ride := Car{"Blue"}
    ride.Change()
    fmt.Println(ride.Color)
}

打印“蓝色”

但是有两个小变化:

func (c *Car) Change() {  // here
    c.Color = "Red"
}

func main() {
    ride := &Car{"Blue"}  // and here
    ride.Change()
    fmt.Println(ride.Color)
}

现在打印出“红色”。 Struct不是引用类型。因此,如果您希望修改结构以跨越墙而不使用返回列表来执行此操作,请使用指针。当然这仅适用于通过参数,返回列表或接收器传递的值;而不是墙壁两侧范围内的变量;或者修改引用类型背后的基础值。

参见Russ Cox的"Pointers Versus Values" in Effective Go"Go Data Structures"