sync.map

这个包很常用, 主要是用于在并发情况下的map...
// Map is like a Go map[interface{}]interface{} but is safe for concurrent use
// by multiple goroutines without additional locking or coordination.
官方的解释 很到位嘛 ...

// The Map type is optimized for two common use cases: (1) when the entry for a given
// key is only ever written once but read many times, as in caches that only grow,
// or (2) when multiple goroutines read, write, and overwrite entries for disjoint
// sets of keys. In these two cases, use of a Map may significantly reduce lock
// contention compared to a Go map paired with a separate Mutex or RWMutex.

翻译一下... map对两种常见的情况进行了优化, 下面这两种情况下 显著提高性能... 牛皮的呢.

  1. 一写多读, 例如cache...
  2. 并发读, 写, 覆写不想交的key集合.
type Map struct {
    mu Mutex  // 锁.
    read atomic.Value //?
    dirty map[interface{}]*entry
    misses
}

对外暴露的方法
func (m *Map) Load(key interface{}) (value interface{}, ok bool) //读
func (m *Map) Store(key, value interface{}) // 写
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) // 存在则读, 不存在则写了之后读, 有点像优化cache的感觉.
func (m *Map) Delete(key interface{}) // 删除
func (m *Map) Range(f func(key, value interface{}) bool) // 遍历Map中所有key.

大概的用法.

package main

import (
    "sync"
    "fmt"
)

func main()  {
    var mTest sync.Map
    // fmt.Println(mTest)
    // 读, 写, 读, 覆写, 读
    fmt.Println(mTest.Load("A"))
    mTest.Store("A", "a")
    fmt.Println(mTest.Load("A"))
    mTest.Store("A", "aa")
    fmt.Println(mTest.Load("A"))

    // 读或者写
    fmt.Println(mTest.LoadOrStore("A", "aa"))
    fmt.Println(mTest.LoadOrStore("B", "b"))
    fmt.Println(mTest.LoadOrStore("B", "bb"))

    // 删,读
    mTest.Delete("A")
    fmt.Println(mTest.Load("A"))
    mTest.Delete("AA")
    fmt.Println(mTest.Load("AA"))

    // 遍历
    mTest.Range(func(key, value interface{}) bool {
        fmt.Println(key, value)
        return true
    })

}

源码分析一下 Map.Store 参考这篇文章, 这个有点牛皮了.
https://www.jianshu.com/p/ec51dac3c65b

func (m *Map) Store(key, value interface{}) {
    read, _ := m.read.Load().(readOnly)                           // 原子读然后类型断言.
    if e, ok := read.m[key]; ok && e.tryStore(&value) {      // key存在就 试着存一下... 
        return  // cas 存入成功就完事大吉了 . 没存成功说明 key 已经被标记删除了 .
    }

    m.mu.Lock()
    read, _ = m.read.Load().(readOnly)   // 加锁之后 重新读一遍, 防止这中间有改变, 上面那次读没加锁.
    if e, ok := read.m[key]; ok {  // key存在
        if e.unexpungeLocked() {     // 将删除标记去除 ..  能走到这说明一定是被标记删除了的 
            // The entry was previously expunged, which implies that there is a
            // non-nil dirty map and this entry is not in it.    // 说明dirty不为空... 将key写入dirty.?
            m.dirty[key] = e            // 去除成功的话就写入entry
        }
        e.storeLocked(&value)     // 改变entry的值,  两个map同时生效.
    } else if e, ok := m.dirty[key]; ok {
        e.storeLocked(&value)
    } else {
        if !read.amended {
            // We're adding the first new key to the dirty map.
            // Make sure it is allocated and mark the read-only map as incomplete.
            m.dirtyLocked()
            m.read.Store(readOnly{m: read.m, amended: true})
        }
        m.dirty[key] = newEntry(value)
    }
    m.mu.Unlock()
}

tryStore 的源码分析

func (e *entry) tryStore(i *interface{}) bool {
    p := atomic.LoadPointer(&e.p)   // 这里原子去读 防止读到脏数据.  
    if p == expunged {   // expunged 是初始化时生产的一个地址 ,  用于作为标记. 
        return false
    }
    for {
        if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {    // 这里使用  CAS 方式  乐观锁. 
            return true
        }
        p = atomic.LoadPointer(&e.p)    // 旧值改变 重新原子读一遍, 重新比较. 
        if p == expunged {
            return false
        }
    }
}

// 这里用到了 atomic 库
// 这个函数的意思是, 如果 *add 等于传入的 old 值, 那么把 new 值赋值给 *add ...
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)

你可能感兴趣的:(sync.map)