我有这个函数:
func New(config *WatcherConfig) *Watcher {
instance := &Watcher{
config: config,
connections: make(map[string]ConnectionInfo),
lock: sync.Mutex{},
}
go instance.Start()
return instance
}
创建一个新的Watcher
实例,当前有两个WatcherConfig
。如您所见,我使用Go例程启动了另一个名为Start()
的函数。
func (w *Watcher) Start() {
fmt.Println(w.config.PathToLogFile)
}
一个WatcherConfig
对PathToLogFile
的值为/var/log/openvpn.log
,另一个WatcherConfig
对PathToLogFile
的值为/var/log/postfix.log
。然而,当我使用GoRoutine调用Start()
函数时,它打印/var/log/postfix.log
两次。如果我删除go例程,像这样:
func New(config *WatcherConfig) *Watcher {
instance := &Watcher{
config: config,
connections: make(map[string]ConnectionInfo),
lock: sync.Mutex{},
}
instance.Start()
return instance
}
现在可以正确打印/var/log/openvpn.log
和/var/log/postfix.log
了
代码调用New()
/// ---------
/// Parse config file
/// ---------
configuration, err := config.ParseConfig(*configFileLocation)
if err != nil {
log.Fatalln(err)
}
var watchers []*watcher.Watcher
for _, watcherConfig := range configuration.Watchers {
watchers = append(watchers, watcher.New(&watcherConfig))
}
为什么go例程"切换";另一个例子?
如何修复:
for _, watcherConfig := range configuration.Watchers {
watcherConfig := watcherConfig // copy
watchers = append(watchers, watcher.New(&watcherConfig))
}
Range循环在Go中是重用迭代变量,即他们重用分配的内存来减少分配,在你的情况下,它是watcherConfig
变量。因此,您通过New(&watcherConfig)
传递给New
的指针将指向相同的内存(因为它被重用)。因此,所有的观察者最终都以相同的配置结束。
在New
中没有运行例程的版本中,只有看起来工作正常,因为print语句输出当前存储在共享内存中的值,但是一旦循环结束,观察者仍然会引用相同的最后一个配置。
有一个FAQ条目讨论了这个问题。