sync.map

作者: 个00个 | 来源:发表于2019-04-19 16:58 被阅读0次

这个包很常用, 主要是用于在并发情况下的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)

相关文章

  • Go语言——sync.Map详解

    Go语言——sync.Map详解 sync.Map是1.9才推荐的并发安全的map,除了互斥量以外,还运用了原子操...

  • go 中的 sync.Map

    sync.map sync.Map 的使用和内置的 map 类型一样,只不过它是并发安全的。 Store:存储一对...

  • go常用包——sync

    内容 Atomic Mutex RWMutex WaitGroup Once Sync.Map Sync.Pool...

  • sync.map

    这个包很常用, 主要是用于在并发情况下的map...// Map is like a Go map[interfa...

  • sync.Map

    场景: 针对一写多读、key不变的情况下高并发的读写 思路: 尽量减少锁 使用两个副本read、dirty,rea...

  • Go sync.Map

    map并发读线程安全,并发读写线程不安全。 sync.Map 读写分离 空间换时间 Map Golang1.6之前...

  • sync.Map && map

    面试题: 为什么map不能并发读写? map 并发读写会panic吗? map + lock 和 sync.Ma...

  • 技术好文集合

    缓存热点key 大value问题,解决方案 golang sync.Map 如何理解互斥锁、条件锁、读写锁以及自旋锁?

  • 看过这篇剖析,你还不懂 Go sync.Map 吗?

    hi, 大家好,我是 haohongfan。 本篇文章会从使用方式和源码角度剖析 sync.Map。不过不管是日常...

  • Go 1.9 sync.Map

    原生的map在没有加锁的情况下用于协程是数据不安全,并且因为不安全,在编译的时候编译器也会报错。如果要在多个协程中...

网友评论

      本文标题:sync.map

      本文链接:https://www.haomeiwen.com/subject/zpviwqtx.html