mgo中的atomic.Value
闲话
最近在刷MIT的分布式课程,线程和锁一定是最基本的元素啦.
由于GO本身提倡的 Share memory by communicating; don't communicate by sharing memory.
,所以在实现的时候试图不用sharing memory + lock而多用channel来实现,这时候就带来了一些小小的不方便,比如一个字符串s可以被多个goroutine读而被一个goroutine写,显然我们要将其加锁.
因为不想加锁于是我试图用atomic来解决这个问题,其中有一个Value类型我发现很有趣,于是决定写下来.
源代码分析
-
atomic.Value分为两个操作,通过Store()存储Value,通过Load()来读取Value的值.
-
源码我就不贴了,贴一个关键的struct:
type ifaceWords struct { typ unsafe.Pointer data unsafe.Pointer }
ifaceWords结构体是实际存储我们的Value值的地方,可以看到,我们存储的实际是指向Value的type和data的指针.
-
Store操作有两种行为模式:
-
First Store : 当我们第一次调用Store的时候,Store函数会初始化typ指针(需要注意的是,每一个Value在第一次Store之后typ就被确定而不能更改了,否则会panic).
如果typ==nil则函数会调用runtime_procPin(没找到实现,但注释中说是active spin wait)
随后调用原子操作函数CompareAndSwapPointer(typ, nil, unsafe.Pointer(^uintptr(0))),如果此时结果返回false说明typ已经不等于nil(被其他goroutine修改过),于是调用runtime_procUnpin解锁并重新进行Store过程.
如果原子操作函数返回了true,即typ == nil,那么存储typ以及data的指针. -
后面的每次Store调用都是直接替换掉data指针
-
-
Load函数检测typ的值,如果为nil或者正在进行首次调用Store则会返回nil.否则返回一个interface{}(实际存储的是ifaceWords值)
用例
-
在MIT的课程中,我们设计的每一个Server都需要访问viewService来获得最新的Primary/Backup数据库服务器视图.
这时候其实对这个View的操作就是周期写,不定时读.这时候就是一个使用Value的合适场景(也可以使用原子函数CompareAndSwapPointer). -
Go官网上给出的例子
type Map map[string]string var m Value m.Store(make(Map)) var mu sync.Mutex // used only by writers // read function can be used to read the data without further synchronization read := func(key string) (val string) { m1 := m.Load().(Map) return m1[key] } // insert function can be used to update the data without further synchronization insert := func(key, val string) { mu.Lock() // synchronize with other potential writers defer mu.Unlock() m1 := m.Load().(Map) // load current value of the data structure m2 := make(Map) // create a new value for k, v := range m1 { m2[k] = v // copy all data from the current object to the new one } m2[key] = val // do the update that we need m.Store(m2) // atomically replace the current object with the new one // At this point all new readers start working with the new version. // The old version will be garbage collected once the existing readers // (if any) are done with it. }