结构和 GoRoutines,切换实例?
Structs and GoRoutines, switches instances?
我有这个功能:
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))
}
Go 中的范围循环 重新使用 迭代变量,即它们重新使用分配的内存以减少分配,在您的情况下是 watcherConfig
多变的。因此,您使用 New(&watcherConfig)
传递给 New
的指针将指向相同的内存(因为它被重新使用)。因此,所有观察者最终都会得到相同的配置。
只有New
中没有goroutine的版本看起来可以正常工作,因为print语句输出的是当前存储在共享内存中的值,但是一旦循环结束时,观察者仍然会引用相同的最后一个配置。
有这个 FAQ entry 讨论了这个问题。
我有这个功能:
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))
}
Go 中的范围循环 重新使用 迭代变量,即它们重新使用分配的内存以减少分配,在您的情况下是 watcherConfig
多变的。因此,您使用 New(&watcherConfig)
传递给 New
的指针将指向相同的内存(因为它被重新使用)。因此,所有观察者最终都会得到相同的配置。
只有New
中没有goroutine的版本看起来可以正常工作,因为print语句输出的是当前存储在共享内存中的值,但是一旦循环结束时,观察者仍然会引用相同的最后一个配置。
有这个 FAQ entry 讨论了这个问题。