4 回复
请展示在main函数中如何实现?比如一个写入和读取的示例
更多关于Golang中如何安全地实现map的并发读写的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我创建了一个封装互斥锁和映射的结构体,这样可以清晰地体现它们之间的关系
type MyMap struct {
sync.RWMutex
internal map[string]int
}
func (m *MyMap) read(key string) (int, boolean) {
m.RLock()
result, ok := m.internal[key]
m.RUnlock()
return result, ok
}
func (m *MyMap) write(key string, value int) {
m.Lock()
m.internal[key] = value
m.Unlock()
}
-
将映射声明更改为
var m *MyMap -
在主函数中初始化 m
m = &MyMap{ internal: map[string]int{ "justin": 19, "james": 28, "juliet": 20, }, } -
将读取和写入协程更改为
func read() { if r, ok := m.read("justin"); ok { fmt.Println(r) } } func write() { m.write("justin", 20) fmt.Println(m.internal) }
在Go语言中,map本身不是并发安全的,直接并发读写会导致运行时panic。你的代码尝试使用sync.Mutex来保护map的并发访问,这是一个正确的方向,但实现上存在一些问题。以下是一个改进后的安全并发读写map的示例,使用sync.RWMutex来优化读写性能:
package main
import (
"fmt"
"sync"
)
type SafeMap struct {
mu sync.RWMutex
data map[string]int
}
func NewSafeMap() *SafeMap {
return &SafeMap{
data: make(map[string]int),
}
}
func (m *SafeMap) Set(key string, value int) {
m.mu.Lock()
defer m.mu.Unlock()
m.data[key] = value
}
func (m *SafeMap) Get(key string) (int, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
value, exists := m.data[key]
return value, exists
}
func (m *SafeMap) Delete(key string) {
m.mu.Lock()
defer m.mu.Unlock()
delete(m.data, key)
}
func main() {
safeMap := NewSafeMap()
var wg sync.WaitGroup
// 并发写入
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := fmt.Sprintf("key%d", i)
safeMap.Set(key, i)
}(i)
}
// 并发读取
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := fmt.Sprintf("key%d", i)
if value, exists := safeMap.Get(key); exists {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
}(i)
}
wg.Wait()
}
这个实现有以下改进:
- 使用
sync.RWMutex替代sync.Mutex,允许多个goroutine同时读取map - 封装了map操作的方法,确保所有访问都通过mutex保护
- 使用
defer语句确保锁一定会被释放 - 提供了完整的Set、Get、Delete操作方法
对于更高性能的场景,可以考虑使用sync.Map,它针对特定并发模式进行了优化:
package main
import (
"fmt"
"sync"
)
func main() {
var sm sync.Map
var wg sync.WaitGroup
// 并发写入
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := fmt.Sprintf("key%d", i)
sm.Store(key, i)
}(i)
}
// 并发读取
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := fmt.Sprintf("key%d", i)
if value, ok := sm.Load(key); ok {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
}(i)
}
wg.Wait()
}
选择哪种方案取决于具体的使用场景:对于读写比例较高的场景,使用sync.RWMutex的自定义结构通常性能更好;对于键值对很少变化且主要进行读取的场景,sync.Map可能更合适。

