【问题标题】:GoRoutines and passing struct to original contextGoRoutines 并将结构传递给原始上下文
【发布时间】:2018-03-13 23:01:35
【问题描述】:

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

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

问题是我能够遍历 thing.Streams.Trades 并从我的 main() 的 for{} 循环中实时查看所有交易。我看不到更新的东西。价格即使有时在处理程序中设置。

我很难理解嵌套结构如何包含数据但不包含价格。我觉得好像我错过了范围界定、goroutines 或可能用于实例化新对象的指针的东西。

任何帮助将不胜感激,同时我将继续阅读。我已将代码简化为看起来相关的内容。

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
}

注意:

我在多个位置添加了一些调试日志记录。在机器人处理程序中,价格被打印(成功),然后更新,然后再次打印(成功)——在 handler() 函数中显示价格设置没有间隙。

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

这种行为是我认为我遗漏了一些非常基本的东西的原因。

【问题讨论】:

  • 刚刚开始,thing.New 返回处理程序的副本。一旦处理程序在同一个结构上运行,您需要添加某种同步,因为这会导致数据竞争。
  • 谢谢,我找到了我认为您正在引用的部分,并删除了取消引用原始 BotHandler 结构的 *。我希望这可能会解决问题,因为它听起来像我遇到的问题,但结果相同。
  • 不,删除 * 会更改接收者类型,因此您还有另一个副本。我会备份并回顾一些基础知识,因为这里有许多基础问题增加了混乱。
  • 感谢 @JimB 抽出宝贵时间发表评论。我从一开始就确保我没有遗漏任何东西,现在我发现我的方法中存在很多问题。

标签: go struct goroutine


【解决方案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)
}

现在它打印“红色”。结构不是引用类型。因此,如果您希望对结构进行修改而不使用返回列表来完成它,请使用指针。当然,这只适用于通过参数、返回列表或接收器传递的值;而不是墙两边范围内的变量;或修改引用类型背后的基础值。

另请参阅 "Pointers Versus Values" in Effective Go 和 Russ Cox 的 "Go Data Structures"

【讨论】:

  • 这对我帮助很大。我不知道退货或收货人会以这种方式行事。谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-11-24
  • 1970-01-01
  • 2017-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-25
  • 1970-01-01
相关资源
最近更新 更多