昨天挖了个并发读写map的坑,今天给填上
mapmapfunc main() {
m := make(map[string]int, 2)
m["dd"] = 22
go func() {
for {
m["ff"] = 1
}
}()
go func() {
for {
_ = m["dd"]
}
}()
time.Sleep(1 * time.Hour)
}
// out:
// fatal error: concurrent map read and map write
fatal error: concurrent map read and map writemap
// runtime.mapassign 即在map进行写入操作时调用的函数
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
// ..............
// 在写入时会对h.flags字段进行正在写入标记以防止并发读写
h.flags ^= hashWriting
// ..........
// 检查是否有并发写的问题
if h.flags&hashWriting == 0 {
throw("concurrent map writes")
}
// 写入完毕回复标记字段
h.flags &^= hashWriting
//.........
}
// runtime.mapaccess1 在 value:=m[key]时调用的函数
func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
// .............
// 检测标记字段 是否不等于0 如果不等于0则代表发生并发读写
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
}
// .............
}
// runtime.mapaccess2 在 value,ok:=m[key]时调用的函数
func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool) {
// .............
// 检测标记字段 是否不等于0 如果不等于0则代表发生并发读写
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
}
// .............
}
mapmapMutexMapmapmaptype MutexMap struct {
lock sync.Mutex
m map[string]int
}
func (receiver *MutexMap) Get(key string) (int, bool) {
receiver.lock.Lock()
value, ok := receiver.m[key]
receiver.lock.Unlock()
return value, ok
}
func (receiver *MutexMap) Set(key string, value int) {
receiver.lock.Lock()
receiver.m[key] = value
receiver.lock.Unlock()
}
func (receiver *MutexMap) Del(key string) {
receiver.lock.Lock()
delete(receiver.m, key)
receiver.lock.Unlock()
}
sync.MutexmapGet(),Set(),Del()mapmapMutexMapRWMutexMapMutexMapmapmappanic读写锁就是一个可以并发读但是不可以并发写的锁,由于互斥锁的特性,会导致将所有goroutine(下称协程)串行化,从而影响整体程序运行的性能,如果写的数量大于读的数量时,性能损耗暂且可以忽略不计(建议不要忽略),但是当读的数量大于写的数量时,互斥锁对性能的影响是很大的,所以此刻我们需要采用读写锁来进行读操作和写操作的分开。
MutexMaptype RWMutexMap struct {
lock sync.RWMutex
m map[string]int
}
func (receiver *RWMutexMap) Get(key string) (int, bool) {
receiver.lock.RLock()
value, ok := receiver.m[key]
receiver.lock.RUnlock()
return value, ok
}
func (receiver *RWMutexMap) Set(key string, value int) {
receiver.lock.Lock()
receiver.m[key] = value
receiver.lock.Unlock()
}
func (receiver *RWMutexMap) Del(key string) {
receiver.lock.Lock()
delete(receiver.m, key)
receiver.lock.Unlock()
}
lock sync.Mutexlock sync.RWMutexGet()RWMutexMapsync.Mapmapsync.Mapsync.Mapmap1.只会增长的缓存系统中,一个 key 只写入一次而被读很多次,即读多写少 2.多个 goroutine 为不相交的键集读、写和重写键值对,即多个goroutineCRUD操作不同的key-value
sync.MapRWMutexMapsync.MapRWMutexMap空间换时间。通过冗余的两个数据结构(只读的 read 字段、可写的 dirty),来减少加锁对性能的影响。对只读字段(read)的操作不需要加锁。 优先从 read 字段读取、更新、删除,因为对 read 字段的读取不需要锁。 动态调整。miss次数多了之后,将 dirty 数据提升为 read,避免总是从 dirty 中加锁读取。 double-checking。加锁之后先还要再检查 read 字段,确定真的不存在才操作 dirty 字段。 延迟删除。删除一个键值只是打标记,只有在提升 dirty字段为 read 字段的时候才清理删除的数据。
sync.MapMutexMapRWMutexMapsync.Mappackage main
import (
"fmt"
"sync"
"testing"
"time"
)
var (
num = 1000 * 10
gnum = 1000
)
func Test_main(t *testing.T) {
count := 10000
div := int(50) //抽样写比例 1/5
fmt.Println("only read")
testRwmutexReadOnly(count)
testMutexReadOnly(count)
//test sync.map
testSyncMapReadOnly(count)
fmt.Println("write and read")
testRwmutexWriteRead(count, div)
testMutexWriteRead(count, div)
testSyncMapWriteRead(count, div)
fmt.Println("write only")
testRwmutexWriteOnly(count)
testMutexWriteOnly(count)
testSyncMapWriteOnly(count)
}
func testRwmutexReadOnly(count int) {
var w = &sync.WaitGroup{}
var rwmutexTmp = newRwmutex(count)
w.Add(gnum)
t1 := time.Now()
for i := 0; i < gnum; i++ {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
rwmutexTmp.get(in)
}
}()
}
w.Wait()
fmt.Println("testRwmutexReadOnly cost:", time.Now().Sub(t1).String())
}
func testRwmutexWriteOnly(count int) {
var w = &sync.WaitGroup{}
var rwmutexTmp = newRwmutex(count)
w.Add(gnum)
t1 := time.Now()
for i := 0; i < gnum; i++ {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
rwmutexTmp.set(in, in)
}
}()
}
w.Wait()
fmt.Println("testRwmutexWriteOnly cost:", time.Now().Sub(t1).String())
}
func testRwmutexWriteRead(count, div int) {
var w = &sync.WaitGroup{}
var rwmutexTmp = newRwmutex(count)
w.Add(gnum)
t1 := time.Now()
for i := 0; i < gnum; i++ {
if i%div != 0 {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
rwmutexTmp.get(in)
}
}()
} else {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
rwmutexTmp.set(in, in)
}
}()
}
}
w.Wait()
fmt.Println("testRwmutexWriteRead cost:", time.Now().Sub(t1).String())
}
func testMutexReadOnly(count int) {
var w = &sync.WaitGroup{}
var mutexTmp = newMutex(count)
w.Add(gnum)
t1 := time.Now()
for i := 0; i < gnum; i++ {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
mutexTmp.get(in)
}
}()
}
w.Wait()
fmt.Println("testMutexReadOnly cost:", time.Now().Sub(t1).String())
}
func testMutexWriteOnly(count int) {
var w = &sync.WaitGroup{}
var mutexTmp = newMutex(count)
w.Add(gnum)
t1 := time.Now()
for i := 0; i < gnum; i++ {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
mutexTmp.set(in, in)
}
}()
}
w.Wait()
fmt.Println("testMutexWriteOnly cost:", time.Now().Sub(t1).String())
}
func testMutexWriteRead(count, div int) {
var w = &sync.WaitGroup{}
var mutexTmp = newMutex(count)
w.Add(gnum)
t1 := time.Now()
for i := 0; i < gnum; i++ {
if i%div != 0 {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
mutexTmp.get(in)
}
}()
} else {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
mutexTmp.set(in, in)
}
}()
}
}
w.Wait()
fmt.Println("testMutexWriteRead cost:", time.Now().Sub(t1).String())
}
func testSyncMapReadOnly(count int) {
var w = &sync.WaitGroup{}
var mutexTmp = newSyncMap(count)
w.Add(gnum)
t1 := time.Now()
for i := 0; i < gnum; i++ {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
mutexTmp.Load(in)
}
}()
}
w.Wait()
fmt.Println("testSyncMapReadOnly cost:", time.Now().Sub(t1).String())
}
func testSyncMapWriteOnly(count int) {
var w = &sync.WaitGroup{}
var mutexTmp = newSyncMap(count)
w.Add(gnum)
t1 := time.Now()
for i := 0; i < gnum; i++ {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
mutexTmp.Store(in, in)
}
}()
}
w.Wait()
fmt.Println("testSyncMapWriteOnly cost:", time.Now().Sub(t1).String())
}
func testSyncMapWriteRead(count, div int) {
var w = &sync.WaitGroup{}
var mutexTmp = newSyncMap(count)
w.Add(gnum)
t1 := time.Now()
for i := 0; i < gnum; i++ {
if i%div != 0 {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
mutexTmp.Load(in)
}
}()
} else {
go func() {
defer w.Done()
for in := 0; in < num; in++ {
mutexTmp.Store(in, in)
}
}()
}
}
w.Wait()
fmt.Println("testSyncMapWriteRead cost:", time.Now().Sub(t1).String())
}
func newRwmutex(count int) *rwmutex {
var t = &rwmutex{}
t.mu = &sync.RWMutex{}
t.ipmap = make(map[int]int, count)
for i := 0; i < count; i++ {
t.ipmap[i] = 0
}
return t
}
type rwmutex struct {
mu *sync.RWMutex
ipmap map[int]int
}
func (t *rwmutex) get(i int) int {
t.mu.RLock()
defer t.mu.RUnlock()
return t.ipmap[i]
}
func (t *rwmutex) set(k, v int) {
t.mu.Lock()
defer t.mu.Unlock()
t.ipmap[k] = v
}
func newMutex(count int) *mutex {
var t = &mutex{}
t.mu = &sync.Mutex{}
t.ipmap = make(map[int]int, count)
for i := 0; i < count; i++ {
t.ipmap[i] = 0
}
return t
}
func newSyncMap(count int) *sync.Map {
var t = &sync.Map{}
for i := 0; i < count; i++ {
t.Store(i, 0)
}
return t
}
type mutex struct {
mu *sync.Mutex
ipmap map[int]int
}
func (t *mutex) get(i int) int {
t.mu.Lock()
defer t.mu.Unlock()
return t.ipmap[i]
}
func (t *mutex) set(k, v int) {
t.mu.Lock()
defer t.mu.Unlock()
k = k % 100
t.ipmap[k] = v
}
// only read
// testRwmutexReadOnly cost: 346.72604ms
// testMutexReadOnly cost: 1.56353484s
// testSyncMapReadOnly cost: 94.436269ms
// write and read
// testRwmutexWriteRead cost: 511.433544ms
// testMutexWriteRead cost: 1.439301381s
// testSyncMapWriteRead cost: 106.220371ms
// write only
// testRwmutexWriteOnly cost: 2.579502195s
// testMutexWriteOnly cost: 1.922190051s
// testSyncMapWriteOnly cost: 4.177583278s
结论:
只读场景:sync.map > rwmutex >> mutex 读写场景(边读边写):rwmutex > mutex >> sync.map 读写场景(读80% 写20%):sync.map > rwmutex > mutex 读写场景(读98% 写2%):sync.map > rwmutex >> mutex 只写场景:sync.map >> mutex > rwmutex
使用环境不负责任推荐
MutexMapRWMutexMapsync.Mapsync.Mapinterface{}RWMutexMapRWMutexMapsync.Mapsync.MapmapRWMutexMapmapRWMutexMapsync.Mapsync.Map