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。请使用syncsync/atomic包中的同步原语来同步对time.Location的访问。”

但是Go语言time包中有很多地方使用了time.Location。例如:time.Now()、time.locabs() 我无法修改这些地方。

那么,我该怎么办?请帮助我。


更多关于Golang中设置time.Location时如何避免数据竞争问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于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包内部的调用,遵循上述避免修改全局时区的模式是更可靠的方法。

回到顶部