可能出现的问题
- 数据竞争与未定义行为:多个协程同时读写Map可能导致数据竞争。在Go语言中,普通的map不是线程安全的,当多个协程同时进行读写操作时,可能会触发运行时错误,报“concurrent map read and map write”或“concurrent map writes”等错误。这是因为map的内部结构在并发读写时可能会被破坏,导致未定义行为。
- 遍历结果的不确定性:Go语言的map遍历顺序是随机的,在高并发场景下,由于读写操作的并发进行,每次遍历得到的结果顺序可能会与期望的顺序(如插入顺序)不一致,这可能导致程序逻辑出现错误。
设计机制保持遍历结果相对稳定(如与插入顺序一致)
- 使用
sync.Map
+ 额外数据结构:
- 原理:
sync.Map
是Go语言标准库提供的线程安全的map。为了保持插入顺序,可以额外使用一个切片来记录插入的键的顺序。
- 示例代码(Go语言):
package main
import (
"fmt"
"sync"
)
type OrderedMap struct {
mu sync.RWMutex
items sync.Map
order []interface{}
}
func (om *OrderedMap) Store(key, value interface{}) {
om.mu.Lock()
defer om.mu.Unlock()
if _, loaded := om.items.Load(key);!loaded {
om.order = append(om.order, key)
}
om.items.Store(key, value)
}
func (om *OrderedMap) Range(f func(key, value interface{}) bool) {
om.mu.RLock()
defer om.mu.RUnlock()
for _, key := range om.order {
if v, ok := om.items.Load(key); ok {
if!f(key, v) {
break
}
}
}
}
- 自定义线程安全的有序Map:
- 原理:基于双向链表和map实现。双向链表用于维护插入顺序,map用于快速查找。通过互斥锁来保证并发安全。
- 示例代码(Go语言):
package main
import (
"fmt"
"sync"
)
type Node struct {
key interface{}
value interface{}
prev *Node
next *Node
}
type OrderedMap struct {
mu sync.RWMutex
root *Node
items map[interface{}]*Node
}
func NewOrderedMap() *OrderedMap {
root := &Node{}
root.prev = root
root.next = root
return &OrderedMap{
root: root,
items: make(map[interface{}]*Node),
}
}
func (om *OrderedMap) Store(key, value interface{}) {
om.mu.Lock()
defer om.mu.Unlock()
if node, ok := om.items[key]; ok {
node.value = value
return
}
newNode := &Node{key: key, value: value}
last := om.root.prev
last.next = newNode
newNode.prev = last
newNode.next = om.root
om.root.prev = newNode
om.items[key] = newNode
}
func (om *OrderedMap) Range(f func(key, value interface{}) bool) {
om.mu.RLock()
defer om.mu.RUnlock()
node := om.root.next
for node != om.root {
if!f(node.key, node.value) {
break
}
node = node.next
}
}