英文:
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
。然而,当我使用Go协程调用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协程会“切换”到另一个实例?
英文:
I have this function:
func New(config *WatcherConfig) *Watcher {
instance := &Watcher{
config: config,
connections: make(map[string]ConnectionInfo),
lock: sync.Mutex{},
}
go instance.Start()
return instance
}
It creates a new instance of Watcher
, currently there are two WatcherConfig
. As you can see I start another function called Start()
using a Go routine.
func (w *Watcher) Start() {
fmt.Println(w.config.PathToLogFile)
}
One WatcherConfig
has a value of /var/log/openvpn.log
for PathToLogFile
and another WatcherConfig
has a value of /var/log/postfix.log
for PathToLogFile
. However when I call the Start()
function using a GoRoutine, it prints /var/log/postfix.log
twice. If I remove the go routine, so like this:
func New(config *WatcherConfig) *Watcher {
instance := &Watcher{
config: config,
connections: make(map[string]ConnectionInfo),
lock: sync.Mutex{},
}
instance.Start()
return instance
}
It will now print /var/log/openvpn.log
and /var/log/postfix.log
correctly.
Code that calls 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))
}
Why does the go routine "switch" to another instance?
答案1
得分: 2
如何修复:
for _, watcherConfig := range configuration.Watchers {
watcherConfig := watcherConfig // 复制
watchers = append(watchers, watcher.New(&watcherConfig))
}
在Go语言中,使用range循环时会重用迭代变量,也就是说它们会重用已分配的内存以减少内存分配。在你的代码中,迭代变量是watcherConfig
。因此,你传递给New
函数的指针New(&watcherConfig)
将指向相同的内存(因为它被重用)。因此,最终所有的watcher都会具有相同的配置。
在New
函数中没有使用goroutine的版本看起来是正确的,因为打印语句输出的是共享内存中当前存储的值,但是一旦循环结束,watchers仍然会引用相同的最后一个配置。
这里有一个常见问题解答,讨论了这个问题。
英文:
How to fix:
for _, watcherConfig := range configuration.Watchers {
watcherConfig := watcherConfig // copy
watchers = append(watchers, watcher.New(&watcherConfig))
}
Range loops in Go are re-using the iteration variable, i.e. they are re-using the allocated memory to reduce allocations, in your case it's the watcherConfig
variable. So the pointer you are passing to New
with New(&watcherConfig)
will point to the same memory (since it is re-used). Hence all watchers end up with the same config at the end.
The version without a goroutine in New
only looks to be working correctly because the print statement outputs the value currently stored in the shared memory, but once the loop ends, the watchers will, nevertheless, have a reference to the same, the last, config.
There's this FAQ entry that discusses the problem.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论