吾八哥博客

您现在的位置是:首页 > 码农手记 > Golang > 正文

Golang

golang里sync.Map包的使用方法

吾八哥2020-05-05Golang3934

sync.Map简介

在Go语言里内置的常规map是只支持并发读,但不支持并发写操作的,常见的错误信息:fatal error: concurrent map writes. 但在golang里已经提供了封装好的线程安全map的包sync.Map,可以看看sync.Map的定义:

// Map is like a Go map[interface{}]interface{} but is safe for concurrent use
// by multiple goroutines without additional locking or coordination.
// Loads, stores, and deletes run in amortized constant time.
//
// The Map type is specialized. Most code should use a plain Go map instead,
// with separate locking or coordination, for better type safety and to make it
// easier to maintain other invariants along with the map content.
//
// 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.
//
// The zero Map is empty and ready for use. A Map must not be copied after first use.
type Map struct {
	mu Mutex

	// read contains the portion of the map's contents that are safe for
	// concurrent access (with or without mu held).
	//
	// The read field itself is always safe to load, but must only be stored with
	// mu held.
	//
	// Entries stored in read may be updated concurrently without mu, but updating
	// a previously-expunged entry requires that the entry be copied to the dirty
	// map and unexpunged with mu held.
	read atomic.Value // readOnly

	// dirty contains the portion of the map's contents that require mu to be
	// held. To ensure that the dirty map can be promoted to the read map quickly,
	// it also includes all of the non-expunged entries in the read map.
	//
	// Expunged entries are not stored in the dirty map. An expunged entry in the
	// clean map must be unexpunged and added to the dirty map before a new value
	// can be stored to it.
	//
	// If the dirty map is nil, the next write to the map will initialize it by
	// making a shallow copy of the clean map, omitting stale entries.
	dirty map[interface{}]*entry

	// misses counts the number of loads since the read map was last updated that
	// needed to lock mu to determine whether the key was present.
	//
	// Once enough misses have occurred to cover the cost of copying the dirty
	// map, the dirty map will be promoted to the read map (in the unamended
	// state) and the next store to the map will make a new dirty copy.
	misses int
}

详细的实现原理在上面的注释里已经写得很清楚了,可以简单理解为通过read和dirty两个字段来实现读写分离、读read数据不加锁,读写dirty才加锁,删除的数据通过标记来实现延迟删除。建议大家去阅读一下sync.Map的源代码。

使用示例

package main

import (
	"fmt"
	"sync"
)

func main() {
	var syncMap sync.Map

	// 添加数据
	syncMap.Store(1, "test-1")
	syncMap.Store(2, "test-2")
	syncMap.Store(3, "test-3")

	// 读取数据
	v, ok := syncMap.Load(1)
	if !ok {
		fmt.Println("数据不存在")
		return
	}
	fmt.Println(v)

	// LoadOrStore方法用于不存在则添加,存在则返回
	fmt.Println(syncMap.LoadOrStore(1, "www.5bug.wang"))

	// 删除数据
	syncMap.Delete(1)

	// 循环遍历读取数据获取长度
	len := 0
	syncMap.Range(func(k, v interface{}) bool {
		len++
		fmt.Println(k, v)
		return true
	})
	fmt.Println("syncMap长度:", len)
}

上面代码输出结果:

test-1
test-1 true
2 test-2
3 test-3
syncMap长度: 2