Golang中设置time.Location时如何避免数据竞争问题
Golang中设置time.Location时如何避免数据竞争问题 我的Go版本是1.11。
当我设置time.Location时,会出现…
==================
WARNING: DATA RACE
Write at 0x000002e275d0 by main goroutine:
vcs.taiyouxi.net/platform/planx/timeutil.SetTimeLocal()
/Users/zhangzhen/serverthreekingdom/src/vcs.taiyouxi.net/platform/planx/timeutil/time_util.go:51 +0xb4
vcs.taiyouxi.net/platform/planx/timeutil.init.0()
/Users/zhangzhen/serverthreekingdom/src/vcs.taiyouxi.net/platform/planx/timeutil/time_util.go:42 +0x43
vcs.taiyouxi.net/platform/planx/timeutil.init()
<autogenerated>:1 +0xd0
vcs.taiyouxi.net/jws2/common/time.init()
<autogenerated>:1 +0xa6
vcs.taiyouxi.net/jws2/gamex/account/account.init()
<autogenerated>:1 +0xa6
vcs.taiyouxi.net/jws2/gamex/logics.init()
<autogenerated>:1 +0xa6
vcs.taiyouxi.net/jws2/gamex/cmds/gamemode.init()
<autogenerated>:1 +0xa6
main.init()
<autogenerated>:1 +0xa6
Previous read at 0x000002e275d0 by goroutine 8:
time.Now()
/Users/zhangzhen/.gvm/gos/go1.11/src/time/time.go:1060 +0xcf
time.sendTime()
/Users/zhangzhen/.gvm/gos/go1.11/src/time/sleep.go:141 +0x44
Goroutine 8 (running) created at:
runtime.(*timersBucket).addtimerLocked()
/Users/zhangzhen/.gvm/gos/go1.11/src/runtime/time.go:170 +0x113
vcs.taiyouxi.net/vendor/github.com/siddontang/go/timingwheel.NewTimingWheel()
/Users/zhangzhen/serverthreekingdom/src/vcs.taiyouxi.net/vendor/github.com/siddontang/go/timingwheel/timingwheel.go:39 +0x2a0
vcs.taiyouxi.net/platform/planx/util.init()
/Users/zhangzhen/serverthreekingdom/src/vcs.taiyouxi.net/platform/planx/util/timer_helper.go:10 +0xf3
vcs.taiyouxi.net/platform/planx/metrics.init()
<autogenerated>:1 +0xbf
vcs.taiyouxi.net/jws2/gamex/cmds/gamemode.init()
<autogenerated>:1 +0x9c
main.init()
<autogenerated>:1 +0xa6
==================
Go语言的特性是goroutine,在不同的goroutine中获取时间是很常见的情况。 但是只能在单个goroutine中设置time.Location。因此数据竞争是不可避免的。
以下是来自agnivade的回复:
“这不是一个bug。请使用sync和sync/atomic包中的同步原语来同步对time.Location的访问。”
但是Go语言time包中有很多地方使用了time.Location。例如:time.Now()、time.locabs() 我无法修改这些地方。
那么,我该怎么办?请帮助我。
更多关于Golang中设置time.Location时如何避免数据竞争问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中设置time.Location时如何避免数据竞争问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中,time.Location的全局设置确实存在数据竞争问题,因为time包内部使用全局变量存储默认时区。当你在一个goroutine中修改time.Local(例如通过time.LoadLocation或直接赋值)而其他goroutine同时调用time.Now()等函数时,就会触发数据竞争警告。
解决方案是避免修改全局的time.Local,而是使用具有特定时区的time.Time对象。以下示例展示如何安全地处理时区,而不修改全局设置:
package main
import (
"fmt"
"time"
)
func main() {
// 加载所需的时区
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
panic(err)
}
// 使用特定时区创建时间,而不是修改全局 time.Local
nowInLoc := time.Now().In(loc)
fmt.Println("当前时间(上海时区):", nowInLoc)
// 或者直接使用时区解析时间字符串
t, err := time.ParseInLocation("2006-01-02 15:04:05", "2023-10-01 12:00:00", loc)
if err != nil {
panic(err)
}
fmt.Println("解析的时间:", t)
}
如果你的代码库中必须设置全局时区(例如遗留代码依赖),使用同步机制来确保在初始化期间设置,并且避免在运行时修改。在init()函数中设置时区,并确保所有goroutine在设置完成后启动:
package main
import (
"sync"
"time"
)
var once sync.Once
func init() {
once.Do(func() {
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
panic(err)
}
time.Local = loc
})
}
然而,这种方法仍然可能在并发初始化时遇到竞争条件,尤其是在导入的包也尝试设置时区的情况下。最佳实践是重构代码,避免依赖全局时区设置,而是显式传递或使用时区参数。例如,在需要时区的函数中接受*time.Location参数:
func getCurrentTime(loc *time.Location) time.Time {
return time.Now().In(loc)
}
对于你提供的错误日志,问题出现在初始化顺序中:多个包的init()函数并发访问了time.Local。确保所有时区相关的设置在一个单独的、早期初始化的包中完成,并使用同步原语控制访问。但鉴于你无法修改time包内部的调用,遵循上述避免修改全局时区的模式是更可靠的方法。

