结构体和Go协程,开关实例?

huangapple go评论107阅读模式
英文:

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)
}

一个WatcherConfigPathToLogFile的值为/var/log/openvpn.log,另一个WatcherConfigPathToLogFile的值为/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.

huangapple
  • 本文由 发表于 2021年10月16日 21:06:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/69595865.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定