atomic库并不能杜绝race冲突
与sync.Map不同,atomic库并不杜绝传统的方式去访问变量。
这样,即使你在所有的写操作中都严格使用atomic.Store类函数,当你用普通方式访问该全局变量时,如=操作,或者在条件语句中的逻辑判断(“==”,"!="之类),依然非互斥的读取了,依然可能与正在写的操作发生冲突。
检查方法
go build -race [其它参数]
编译好的程序一运行就发现了WARNING: DATA RACE
你可以说,这只是个警告而已。但是在实际运行中,万一这读写操作同时发生了,就会立即崩溃(panic)。
我的改进
简单粗暴的把数据扩充成带锁的,读写都必须通过特殊用法(函数)
package main
import (
"fmt"
"sync"
)
\\\\以下这段可以单做成一个global package
type Int struct{
mutex sync.Mutex
value int
}
func (I *Int) Save(n int){
I.mutex.Lock()
I.value = n
I.mutex.Unlock()
}
func (I *Int) Load() int{
I.mutex.Lock()
defer I.mutex.Unlock()
return I.value
}
\\\\\\\\\\\\\\\
var S = &Int{}
func main() {
S.Save(5)
fmt.Printf("S=%v, S.value=%d \n", S, S.value)
fmt.Println("S:", S.Load())
}
现在所有的全局变量都照此办理,只不过针对不同类型,需要补充struct外包类型,以及相应的用法函数。
而且一旦这些定义单放在一个库,如起名global中,即使S想改写或读取,都不可能直接调用S.value, 因为Int类型是在另一个库,且里面的内容value是小写,私有的禁止跨库调用,则调用只能通过Save或Load这些用法了。
也就是只要用了此种方法,工程中任何场合的该变量必须照此办理,就杜绝了隐患。
最新版的go1.18已经采用
经过我发文来挑战现有问题
A patch to avoid point-time race coincidence like Rust
最新的atomic库已经实现了类似功能
Improving safe global variables with generic or inheritance
只要你的go版本能顺利编译并运行如下代码,输出0,就是有此功能的:
package main
import (
"fmt"
"sync/atomic"
)
var a = atomic.Int32{}
func main() {
fmt.Println(a.Load())
}