Go中并发写共享变量不安全,count++等非原子操作必出错,须用sync.Mutex(通用)、atomic(整数/指针)或os.Rename(文件)等显式同步机制保障安全。

Go 里并发写入共享变量不自动安全,count++ 这种操作在多 goroutine 下必然出错,必须显式加同步机制。
用 sync.Mutex 保护非原子写操作
适用于任意类型、任意写逻辑(比如更新 map、修改结构体字段、拼接字符串等),是最通用的方案。
-
Lock()和Unlock()必须成对出现;漏掉Unlock()就会死锁,尤其注意函数有多个return路径时 - 推荐写法:
mu.Lock(); defer mu.Unlock(),靠 defer 保证释放 - 锁粒度要小——只包裹真正读写的那几行,别把日志、HTTP 请求之类无关代码包进去
- 含
sync.Mutex的结构体不能直接复制(mu是不可拷贝类型),传指针
用 sync/atomic 替代简单整数/指针操作
仅限 int32、int64、uintptr、unsafe.Pointer 等少数类型,性能比 mutex 高,但不能用于结构体或切片。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
atomic.AddInt32(&x, 1)比x++安全,且是原子的 -
atomic.LoadInt32(&x)/atomic.StoreInt32(&x, 10)保证读写可见性,避免编译器重排或 CPU 缓存不一致 -
atomic.CompareAndSwapInt32(&x, old, new)适合实现无锁算法,但要注意 ABA 问题 - 指针赋值如
p = &v本身不原子,要用atomic.StorePointer(&p, unsafe.Pointer(&v))
写文件时避免并发覆盖:临时文件 + os.Rename
这是唯一真正跨平台、原子性有保障的方案,比 flock 更可靠(flock 在 Windows 不可用,且只是建议锁)。
立即学习“go语言免费学习笔记(深入)”;
- 每个 goroutine 写到独立临时文件,路径带随机后缀或 PID,例如
log.tmp.12345 - 写完调用
os.Rename(tmpPath, finalPath)—— 同一文件系统下该操作 POSIX 保证原子 - 必须确保
tmpPath和finalPath在同一目录,否则Rename会退化为 copy+remove,失去原子性 - 程序启动时扫描并清理残留
*.tmp.*文件,避免磁盘占满
真正容易被忽略的是:原子操作不是万能的,atomic 只保单个操作,不保逻辑组合;os.Rename 的原子性依赖同目录约束;而 sync.Mutex 如果锁住太多东西,反而会把并发变成串行。选哪个,得看你在写什么、写多少、谁在读、要不要跨平台。

















