golang里sync.Map包的使用方法
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