我有一个配置,它定义了一些实例(SomeConfigItems),它们为每个实例创建了一个thing()。
该东西是包含的包返回的结构,其中包含Price(float64)和嵌套结构。嵌套的结构维护着一个交易图。
问题是我能够遍历thing.Streams.Trades并查看我的main()为{}循环实时发生的所有交易。我无法看到更新的东西。即使它偶尔设置在Handler中也是如此。
我很难理解嵌套结构如何包含数据但不包含Price。我觉得好像缺少了一些范围,goroutine或者可能用于实例化新对象的指针。
任何帮助将不胜感激,我会在此期间继续阅读。我已将代码缩减为相似的内容。
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
}
}
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.
}
}
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。
这种行为就是为什么我认为我错过了一些非常基本的东西。
答案 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"。