当前位置:   article > 正文

golang的map是不是并发安全的?怎么保证安全?

golang的map是不是并发安全的?怎么保证安全?

参考链接
https://www.iamshuaidi.com/23354.html

map 并不是并发安全的

map是引用类型,如果两个map同时指向一个底层,那么一个map的变动会影响到另一个map。 map 的并发不安全主要是因为缺乏内置的锁机制和原子操作,并且在扩容过程中可能会导致数据不一致。

  • 锁机制: map 的底层数据结构是一个哈希表,当多个 Goroutine 并发地对 map 进行读写操作时,可能会导致数据竞争(DataRace)。因为 map 没有内置的锁机制来保护并发访问,所以在并发读写时可能会造成数据不一致或损坏。

  • 原子操作: 尽管 map 的操作看起来是单个的语句,比如 m[key] = value 和 delete(m,key),但实际上它们都不是原子操作。在并发环境中,一个 map 操作可能会由多个机器指令组成,而且在执行期间可能会被中断或切换到另一个Goroutine,这就增加了出错的可能性。

  • 扩容操作【重点】: 当 map 的元素个数超过了当前容量的时候,Go 语言会自动扩容map,这时会重新分配内存,并将原有的键值对重新哈希到新的内存位置。在扩容过程中,如果有其他 Goroutine并发地进行读写操作,可能会导致指针失效、丢失数据或者死锁等问题。

因此,这意味着在多个 Goroutine 同时读取和写入 map 时可能会导致竞态条件(Race Condition)和数据竞争(Data Race)。当多个 Goroutine 并发地读取和写入同一个 map 时,可能会出现不可预期的结果,甚至导致程序崩溃。

要保证 map 的并发安全,可以采取以下几种方式:

使用互斥锁(Mutex)
使用读写互斥锁(RWMutex)
使用并发安全的数据结构:sync.Map

1. 使用互斥锁(Mutex)

可以在读取和写入 map 的操作上加锁,保证同一时刻只有一个 Goroutine 能够访问 map。通过 sync.Mutex 提供的锁机制可以很容易地实现这一点。

import "sync"

var mu sync.Mutex
var m = make(map[string]int)

// 写操作
mu.Lock()
m["key"] = value
mu.Unlock()

// 读操作
mu.Lock()
value := m["key"]
mu.Unlock()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2. 使用读写互斥锁(RWMutex):

如果多个 Goroutine 需要同时读取 map,但只有一个 Goroutine 写入 map,可以使用 sync.RWMutex 提供的读写锁机制。这种方式可以提高并发读取的效率。

import "sync"

var mu sync.RWMutex
var m = make(map[string]int)

// 写操作
mu.Lock()
m["key"] = value
mu.Unlock()

// 读操作
mu.RLock()
value := m["key"]
mu.RUnlock()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3. 使用并发安全的数据结构:

在 Go 语言的 sync 包中还提供了一些并发安全的数据结构,例如 sync.Map。使用 sync.Map 可以避免手动管理锁,简化并发编程。

import "sync"

var m sync.Map

// 写操作
m.Store("key", value)

// 读操作
value, ok := m.Load("key")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

总的来说,要保证 map 的并发安全,需要在并发访问时进行适当的同步控制,使用锁机制或并发安全的数据结构来保护 map 的读写操作。

===========================
额外知识:

  1. map是引用类型,如果两个map同时指向一个底层,那么一个map的变动会影响到另一个map。
  2. map的零值(Zero Value)是nil,对nil map进行任何添加元素的操作都会触发运行时错误(panic)。因此,使用前必须先创建map,使用make函数,例如:m := make(map[string]int)。
  3. map的键可以是任何可以用 == 或!=操作符比较的类型,如字符串,整数,浮点数,复数,布尔等。但是,slice,map,和function类型不可以作为map的键,因为这些类型不能使用==或!=操作符进行比较。
  4. map在使用过程中不保证遍历顺序,即:map的遍历结果顺序可能会不一样,所以在需要顺序的场合,要自行处理数据并排序。
  5. map进行的所有操作,包括读取,写入,删除,都是不安全的。也就是说,如果你在一个goroutine中修改map,同时在另一个goroutine中读取map,可能会触发“concurrent map read and map write”的错误。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/150333
推荐阅读
  

闽ICP备14008679号