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.
    }
相關文章
相關標籤/搜索