zoukankan      html  css  js  c++  java
  • Golang:sync.Map

    由于map在gorountine 上不是安全的,所以在大量并发读写的时候,会出现错误。

    在1.9版的时候golang推出了sync.Map.

    sync.Map

    通过阅读源码我们发现sync.Map是通过冗余的两个数据结构(read、dirty),实现性能的提升。

    为了提升性能,load、delete、store等操作尽量使用只读的read;

    为了提高read的key命中概率,只有当read中读取不到的累计miss次数大于等于dirty的长度时,将dirty数据提升为read;

    对于数据的删除,采用延迟标记删除法,只有在提升dirty的时候才删除。

    数据结构

     1   type Map struct {
     2     // 读写dirty时使用的锁
     3     mu Mutex
     4     read atomic.Value 
     5     dirty map[interface{}]*entry
     6     // 从read中读取不到,从dirty读取到数据时,+1
     7     misses int
     8 }
     9 
    10 type readOnly struct {
    11     m  map[interface{}]*entry
    13     amended bool 
    14 }
    15 
    16 type entry struct {
    17     //指针类型
    18     p unsafe.Pointer
    19 }

    Delete

     1 func (m *Map) Delete(key interface{}) {
     2     read, _ := m.read.Load().(readOnly)
     3     e, ok := read.m[key]
     4     if !ok && read.amended {
     5         m.mu.Lock()
     6         read, _ = m.read.Load().(readOnly)
     7         e, ok = read.m[key]
     8         if !ok && read.amended { //double check
     9             delete(m.dirty, key)
    10         }
    11         m.mu.Unlock()
    12     }
    13     if ok {
    14         e.delete()
    15     }
    16 }
    
    
     1 func (e *entry) delete() (hadValue bool) {
     2     for {
     3         p := atomic.LoadPointer(&e.p)
     4         if p == nil || p == expunged {
     5             return false
     6         }
     7         if atomic.CompareAndSwapPointer(&e.p, p, nil) { //原子操作,加删除标记
     8             return true
     9         }
    10     }
    11 }


    删除时,如果read中没有,就直接从dirty删除。如果read中有,就把read中标记为删除。

     Load

     1 func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
     2     read, _ := m.read.Load().(readOnly)
     3     e, ok := read.m[key]
     4     if !ok && read.amended {
     5         m.mu.Lock()
     6         read, _ = m.read.Load().(readOnly)
     7         e, ok = read.m[key]
     8         if !ok && read.amended {
     9             e, ok = m.dirty[key] // read中读取不到,从dirty读,miss++
    10             m.missLocked()
    11         }
    12         m.mu.Unlock()
    13     }
    14     if !ok {
    15         return nil, false
    16     }
    17     return e.load()
    18 }

    Load返回存储在映射中的键值(read中读取不到,从dirty读),如果没有值,则返回nil。ok结果指示是否在映射中找到值。

    missLocked和Store

    1 func (m *Map) missLocked() {
    2     m.misses++
    3     if m.misses < len(m.dirty) {
    4         return
    5     }
    6     m.read.Store(readOnly{m: m.dirty})
    7     m.dirty = nil
    8     m.misses = 0
    9 }
     1 func (m *Map) Store(key, value interface{}) {
     2     //如果在read 读取到,就原子操作直接对值进行更新
     3     read, _ := m.read.Load().(readOnly)
     4     if e, ok := read.m[key]; ok && e.tryStore(&value) {
     5         return
     6     }
     7 
     8     //如果未在read 中读取到值或读取到值进行更新时更新失败,则加锁进行后续处理
     9     m.mu.Lock()
    10     read, _ = m.read.Load().(readOnly)
    11     if e, ok := read.m[key]; ok {
    12         // double check,如果读取到的值处于删除状态,将值写入dirty map中
    13         if e.unexpungeLocked() {
    14             m.dirty[key] = e
    15         }
    16         // 原子操作更新key对应的值
    17         e.storeLocked(&value)
    18     } else if e, ok := m.dirty[key]; ok {
    19         //如果在dirty map中读取到值,则直接使用原子操作更新值
    20         e.storeLocked(&value)
    21     } else {
    22         //如果dirty map中不含有值,则说明dirty map已经升级为read map,或者第一次进入
    23         //需要初始化dirty map,并将read map的key添加到新创建的dirty map中.
    24         if !read.amended {
    25             m.dirtyLocked()
    26             m.read.Store(readOnly{m: read.m, amended: true})
    27         }
    28         m.dirty[key] = newEntry(value)
    29     }
    30     m.mu.Unlock()
    31 }

    一些观点,当有大量并发读写发生的时候,会有很多的miss导致不断的dirty升级。可能会影响效率。

    尝试使用sync.Map

     1 package main
     2 
     3 import (
     4     "fmt"
     5     "sync"
     6 )
     7 
     8 func main() {
     9     var m sync.Map
    10     m.Store(1, "a")
    11     m.Store(2, "b")
    12     m.Store(3, "c")
    13     m.Store(4, "d")
    14 
    15     m.Range(func(k, v interface{}) bool {
    16         fmt.Println(k, v)
    17         return true
    18     })
    19     //LoadOrStore
    20     v, ok := m.LoadOrStore(5, "e")
    21     fmt.Println(ok, v)
    22 
    23     v, ok = m.LoadOrStore(1, "bbb")
    24     fmt.Println(ok, v)
    25 
    26     //Load
    27     v, ok = m.Load(1)
    28     if ok {
    29         fmt.Println("it's an existing key,value is ", v)
    30     } else {
    31         fmt.Println("it's an unknown key")
    32     }
    33 
    34     m.Range(func(k, v interface{}) bool {
    35         fmt.Println(k, v)
    36         return true
    37     })
    38 
    39     m.Delete(1)
    40     fmt.Println(m.Load(1))
    41 
    42 }
     1 4 d
     2 1 a
     3 2 b
     4 3 c
     5 false e
     6 true a
     7 it's an existing key,value is  a
     8 2 b
     9 3 c
    10 4 d
    11 5 e
    12 1 a
    13 <nil> false

    运行结果是:

  • 相关阅读:
    IIS无法显示 XML 页
    asp.net实现sql存取图片
    微软的面试题
    IIS配置.Net问题大全
    Asp.Net调用WebService
    生活本无常,前路更精彩
    【转载】碰到讨厌的老板怎么办
    xxxx不必xx尽,留些xxxx
    【BLOG】Mr梵谷
    机会,从来都是留给有准备的你
  • 原文地址:https://www.cnblogs.com/13579net/p/10115046.html
Copyright © 2011-2022 走看看