昨天挖了个并发读写map的坑,今天给填上

map
map
func 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")
 }
 // .............


map
map
MutexMap
mapmap
type 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()mapmap
MutexMapRWMutexMap
MutexMapmapmappanic

读写锁就是一个可以并发读但是不可以并发写的锁,由于互斥锁的特性,会导致将所有goroutine(下称协程)串行化,从而影响整体程序运行的性能,如果写的数量大于读的数量时,性能损耗暂且可以忽略不计(建议不要忽略),但是当读的数量大于写的数量时,互斥锁对性能的影响是很大的,所以此刻我们需要采用读写锁来进行读操作和写操作的分开。

MutexMap
type 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()RWMutexMap
sync.Map
mapsync.Mapsync.Mapmap

1.只会增长的缓存系统中,一个 key 只写入一次而被读很多次,即读多写少 2.多个 goroutine 为不相交的键集读、写和重写键值对,即多个goroutineCRUD操作不同的key-value

sync.MapRWMutexMapsync.MapRWMutexMap
  1. 空间换时间。通过冗余的两个数据结构(只读的 read 字段、可写的 dirty),来减少加锁对性能的影响。对只读字段(read)的操作不需要加锁。
  2. 优先从 read 字段读取、更新、删除,因为对 read 字段的读取不需要锁。
  3. 动态调整。miss次数多了之后,将 dirty 数据提升为 read,避免总是从 dirty 中加锁读取。
  4. double-checking。加锁之后先还要再检查 read 字段,确定真的不存在才操作 dirty 字段。
  5. 延迟删除。删除一个键值只是打标记,只有在提升 dirty字段为 read 字段的时候才清理删除的数据。
sync.MapMutexMapRWMutexMapsync.Map
package 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.Map
sync.Mapinterface{}RWMutexMapRWMutexMapsync.Mapsync.MapmapRWMutexMapmapRWMutexMapsync.Mapsync.Map