golang源码阅读-sync.map

1. sync.map简介

golang内置的map不是并发安全的,在v1.9版本提供了并发安全的map: sync.map

2. 简单使用

func TestSyncMap(t *testing.T) {
	m := sync.Map{}
	m.Store("name", "xiaoming")
	if item, ok := m.Load("name"); ok {
		fmt.Println(item)
	}
	m.Delete("name")
}

sync.map的使用方式和map类似,数据存储、获取、删除和遍历

3. sync.map设计思想

如果使用map 加锁进行数据操作时,耗时相对较多,sync.map采用了对数据进行读写分离,读时不加锁,用空间换时间

4.  sync.map

type Map struct {
// 锁
	mu Mutex
// 存储仅读数据
	read atomic.Value // readOnly
// 存储写入数据
	dirty map[any]*entry
// 计数器
	misses int
}

进行read操作时,不需要加锁,数据类型为atomic.Value,保证了数据的获取和将dirty赋值给read时的原子性,数据存储的是readOnly, readOnly内部存储的是值的指针,和dirty值的指针一致

5. 数据存储

func (m *Map) Store(key, value any) {
// 先从read中查找key
// 如果找到数据则更新数据, read和dirty数据存储的都是指针,修改其中一个值,另外一个同时被修改
// 如果在read中数据被标记为删除,则继续查找
	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 {
// 如果read的数据被标记为删除,则将数据设置为nil
		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
			m.dirty[key] = e
		}
// 存储最新数据
		e.storeLocked(&value)
	} else if e, ok := m.dirty[key]; ok {
// read中没有,dirty中有对应的key,更新数据
		e.storeLocked(&value)
	} else {
// read和dirty都没有时
// amended 表示read和dirty数据是否不一致
// 数据一致时
		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.
// 将read未被标记删除的数据,刷新到dirty中
			m.dirtyLocked()
// 
			m.read.Store(readOnly{m: read.m, amended: true})
		}
// dirty存储新值
		m.dirty[key] = newEntry(value)
	}
	m.mu.Unlock()
}

6. 数据获取

func (m *Map) Load(key any) (value any, ok bool) {
	read, _ := m.read.Load().(readOnly)
	e, ok := read.m[key]
// 数据不在read中,并且read和dirty数据不一致
	if !ok && read.amended {
		m.mu.Lock()
		// Avoid reporting a spurious miss if m.dirty got promoted while we were
		// blocked on m.mu. (If further loads of the same key will not miss, it's
		// not worth copying the dirty map for this key.)
// 双检查
		read, _ = m.read.Load().(readOnly)
		e, ok = read.m[key]
		if !ok && read.amended {
// 直接从dirty中获取
			e, ok = m.dirty[key]
			// Regardless of whether the entry was present, record a miss: this key
			// will take the slow path until the dirty map is promoted to the read
			// map.
// 每次在read中没有查到并且两者数据不一致,进行计数,达到阈值后,将dirty复制给read, dirty设置为nil
			m.missLocked()
		}
		m.mu.Unlock()
	}
	if !ok {
		return nil, false
	}
// 返回存在的数据
	return e.load()
}

7. 数据删除

func (m *Map) LoadAndDelete(key any) (value any, loaded bool) {
	read, _ := m.read.Load().(readOnly)
	e, ok := read.m[key]
// read中没有,并且read和dirty数据不一致
	if !ok && read.amended {
		m.mu.Lock()
// 加锁后 双检查
		read, _ = m.read.Load().(readOnly)
		e, ok = read.m[key]
		if !ok && read.amended {
// 直接删除dirty
			e, ok = m.dirty[key]
			delete(m.dirty, key)
			// Regardless of whether the entry was present, record a miss: this key
			// will take the slow path until the dirty map is promoted to the read
			// map.
// read中数据不存在并且 read和dirty数据不一致进行计数,达到阈值后,将dirty复制给read,dirty设置为nil
			m.missLocked()
		}
		m.mu.Unlock()
	}
	if ok {
		return e.delete()
	}
	return nil, false
}

你可能感兴趣的:(golang,golang,开发语言,后端)