并行读写map
- 这段代码存在并发读写map的问题,即使操作的key不同,也会导致数据竞争。
- map的并发不安全特性:Go语言原生的map不支持并发读写操作。即使对不同key进行操作,底层数据结构仍可能发生修改(如rehash),这会引发panic。
- 两个goroutine同时对同一个map进行读写操作,触发
fatal error: concurrent map read and map write
- map的元数据(如桶数量、扩容状态、哈希种子等)是全局的。例如:1、当写入操作触发扩容(rehash)时,整个哈希表会重新分配桶并迁移数据。此时,即使操作的是不同的key,所有读写操作都可能访问到正在变化的元数据或桶结构,导致不一致。2、扩容过程中,旧桶和新桶的状态可能同时被修改,其他协程若并发访问,可能读取到中间状态的数据。
- 并行读map是允许的
- 并行写map是不允许的,会导致脏数据,
fatal error: concurrent map writes
- 既然不允许并行,那就用锁的形式,把读写串行起来!
- golang中 sync.Map{} 本身就是支持发读写的。
- 在Go语言中,sync.Map 是标准库 sync 包提供的一个并发安全的键值对映射结构。它专为高并发场景设计,避免了传统 map 结合互斥锁(如 sync.Mutex)时的锁竞争问题。
- 直接声明 var m sync.Map 即可使用,无需显式初始化。
- Store(key, value): 存储键值对。
- Load(key): 读取键对应的值,返回 (value, bool)。
- LoadOrStore(key, value): 若键存在则返回现有值,否则存储并返回给定值。
- Delete(key): 删除键。
- Range(func(key, value) bool): 遍历所有键值对,遍历时可通过返回 false 提前终止。
- 但是有些情况下用sync.Map来计数也会出现问题
- 高并发下的两个协程同时执行同一段代码,会出现脏写问题
并行读写slice
- 切片支持并发读写,至少说,不会报 fatal error
- 但是slice并不保证并发读写后的正确性
- 代码运行起来后会发现实际输出的结果远远小于我们的预期,因为在高并发的场景下会有很多操作重复了,在原始的数据在去执行加1操作,导致脏数据的产生
- 如果确实要并发处理slice,最好是事先约定好每个协程负责处理slice的一个部分,且各个协程负责的部分不要有交集!!!