相同的指针不同的值,在多文件主包



我的项目是一个登录注册web服务器,由多个文件组成,并使用另一个包,其中定义了Manager结构体。

我的文件概述:

my-package/
main.go
handlers.go
...

我有一个变量:var M *Managermain()定义之前在main.go中声明,并在main()中赋值:

var M *Manager
func main() {
...
M = InitManager(...)
...
}

handleLogin(...)handleRegister(...)是在handlers.go中定义的使用M变量的函数:

func handleRegister(...){
...
fmt.Println("M:", M)
M.Log1("logging informations...")
...
}

func handleLogin(...) {
...
fmt.Println("M:", M)
M.GetAccount(login)
...
}

当我转到/login/register并触发相应的句柄函数时,它显示:M: <nil>

为了找出更多的东西,我修改了main()如下所示:

var M *Manager
func main() {
...
go func() {  // for debugging
for {
fmt.Println("main() goloop1: M:", M)
time.Sleep(time.Second / 2)
}
}()
M = InitManager(...)
go func() {  // for debugging
for {
fmt.Println("main() goloop2: M:", M)
time.Sleep(time.Second / 2)
}
}()
...
}

和输出:

main() goloop2: M: &{...data as expected...}
main() goloop1: M: <nil>
main() goloop2: M: &{...data as expected...}
main() goloop1: M: <nil>
...
我的问题是:
  1. 如果一个指针给出两个值,那么指针是如何工作的?
  2. 如何解决我的问题,并妥善规划代码(如果这是原因),以避免这种未来?

根据Go内存模型,所提供的代码在没有适当同步的情况下写入和读取M,这是一种数据竞争并导致未定义的行为(参见icza的注释)。

编译器假定"代码是正确同步的(这是开发人员的责任),因此它是"允许的"。假设M在无限循环中永远不会被修改,因此它可能会反复使用给定寄存器或堆栈内存位置的副本,从而导致令人惊讶的输出。

你可以使用同步。互斥锁保护对全局*Manager变量M的每次访问,就像修改后的代码一样。

还要注意变量阴影!可以用M := f()代替M = f(),产生一个不相关的局部变量,而不影响全局变量。

我的解决方案:

我添加了init.go:

my-package/
main.go
handlers.go
...
init.go

我把全局变量像MCONFIG_MAP移动到init.go文件:

package main
import ...
var CONFIG_MAP = LoadConfig()
var M *asrv.Manager = InitManager()
func LoadConfig() map[string]string {
// return map from 'conf.json' file
}
func InitManager() *asrv.Manager {
// return Manager configured with CONFIG_MAP
// other functions also use CONFIG_MAP that is the reason why it is global
}
func init() {
LoadTemplatesFiles() // load templates and assign value
// to a variable declared in templates.go
}

这样handlers.go中的处理器函数(handleLoginGet等)可以正确读取M

这个修复只是使我的程序工作,我仍然不知道处理这种情况的正确方法是什么,这就是为什么我在EDIT下添加了更多的信息在我的问题

最新更新