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的指针.ui

    • 后面的每次Store调用都是直接替换掉data指针this

  • Load函数检测typ的值,若是为nil或者正在进行首次调用Store则会返回nil.不然返回一个interface{}(实际存储的是ifaceWords值)atom

用例

  • 在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.
    }
相关文章
相关标签/搜索