go1.9新版本 并发安全的map使用及内部实现

诺唯 · · 1001 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Golang 1.9版本还没有发布,大概在8月中旬发布,于是我看了下新版本都有哪些新东西,看来看去,看到了并发安全的Map于是我就看了一下源码,本篇文章将带领大家,看看新版本中 的并发安全的Map到底是个什么样子

软件包中 的新Map类型sync是具有存储和删除的并发映射。多个goroutines可以同时调用Map的方法是安全的。

在新版本中官方是这样描述的:

The new Map type in the sync package is a concurrent map with amortized-constant-time loads, stores, and deletes. It is safe for multiple goroutines to call a Map's methods concurrently.

使用方法

1.9以前都是自己通过锁来处理map 的并发安全的并封装成新的struct暴露给外部使用的,一下代码是1.9的代码使用案例:

func main() {
	list := map[string]interface{}{
		"name":          "田馥甄",
		"birthday":      "1983年3月30日",
		"age":           34,
		"hobby":         []string{"听音乐", "看电影", "电视", "和姐妹一起讨论私人话题"},
		"constellation": "白羊座",
	}

	var m sync.Map
	for k, v := range list {
		m.Store(k, v)
	}

	var wg sc.WaitGroup
	wg.Add(2)
	go func() {
		m.Store("age", 22)
		m.LoadOrStore("tag", 8888)
		wg.Done()
	}()

	go func() {
		m.Delete("constellation")
		m.Store("age", 18)
		wg.Done()
	}()

	wg.Wait()

	m.Range(func(key, value interface{}) bool {
		fmt.Println(key, value)
		return true
	})
}

暴露出来了Map结构是开箱即用的,也可以声明这样的0值

var m sync.Map

sync包中暴露出来的map方法有:

func (m *Map) Load(key interface{}) (interface{},bool)
func (m *Map) Store(key, value interface{})
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
func (m *Map) Delete(key interface{}) 
func (m *Map) Range(f func(key, value interface{}) bool)

内部实现

接下来分别分析一下上面暴露出来的方法的内部实现,先查看一下sync.Map结构(sync.Map结构中使用了atomic.Value作为并发安全的替代方法,因为在使用atomic.Value的时候 ,一旦Store被调用那么atomic.Value 就不能被复制,复制后会出现一些竞争问题,所以在使用sync.Map 的时候,在初始化之后也是不要复制sync.Map):

type Map struct {
	mu sync.Mutex
	read atomic.Value 
	dirty map[interface{}]*entry
	misses int
}



func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
    read, _ := m.read.Load().(readOnly)
    e, ok := read.m[key]
    if !ok && read.amended {
      m.mu.Lock()
      read, _ = m.read.Load().(readOnly)
      e, ok = read.m[key]
      if !ok && read.amended {
        e, ok = m.dirty[key]
        m.missLocked()
      }
      m.mu.Unlock()
    }
    if !ok {
      return nil, false
    }
    return e.load()
}


func (m *Map) Store(key, value interface{}) {
    read, _ := m.read.Load().(readOnly)
    if e, ok := read.m[key]; ok && e.tryStore(&value) {
      return
    }
  
    m.mu.Lock()
    read, _ = m.read.Load().(readOnly)
    if e, ok := read.m[key]; ok {
      if e.unexpungeLocked() {
        m.dirty[key] = e
      }
      e.storeLocked(&value)
    } else if e, ok := m.dirty[key]; ok {
      e.storeLocked(&value)
    } else {
      if !read.amended {
        m.dirtyLocked()
        m.read.Store(readOnly{m: read.m, amended: true})
      }
      m.dirty[key] = newEntry(value)
    }
    m.mu.Unlock()
  }





func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) {
    read, _ := m.read.Load().(readOnly)
    if e, ok := read.m[key]; ok {
      actual, loaded, ok := e.tryLoadOrStore(value)
      if ok {
        return actual, loaded
      }
    }
  
    m.mu.Lock()
    read, _ = m.read.Load().(readOnly)
    if e, ok := read.m[key]; ok {
      if e.unexpungeLocked() {
        m.dirty[key] = e
      }
      actual, loaded, _ = e.tryLoadOrStore(value)
    } else if e, ok := m.dirty[key]; ok {
      actual, loaded, _ = e.tryLoadOrStore(value)
      m.missLocked()
    } else {
      if !read.amended {
        m.dirtyLocked()
        m.read.Store(readOnly{m: read.m, amended: true})
      }
      m.dirty[key] = newEntry(value)
      actual, loaded = value, false
    }
    m.mu.Unlock()
  
    return actual, loaded
  }



func (m *Map) Delete(key interface{}) {
    read, _ := m.read.Load().(readOnly)
    e, ok := read.m[key]
    if !ok && read.amended {
      m.mu.Lock()
      read, _ = m.read.Load().(readOnly)
      e, ok = read.m[key]
      if !ok && read.amended {
        delete(m.dirty, key)
      }
      m.mu.Unlock()
    }
    if ok {
      e.delete()
    }
}

有疑问加站长微信联系(非本文作者)

本文来自:知乎专栏

感谢作者:诺唯

查看原文:go1.9新版本 并发安全的map使用及内部实现

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1001 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传