
Go 语言的并发模型鼓励“通过通信共享内存,而不是通过共享内存来通信”。对于内置的 map 类型,Go 官方经过深思熟虑,决定不默认提供并发安全的访问机制。其主要考量如下:
为了在多 goroutine 环境下安全地使用 map,我们必须引入适当的同步机制来协调对 map 的访问。以下是两种常用的方法:
这是最直接且普遍的同步方式,通过互斥锁来保护 map 的读写操作,确保在任何给定时刻只有一个 goroutine 能够修改 map。
原理:sync.Mutex 是一个互斥锁,任何时候只允许一个 goroutine 获得锁并访问被保护的资源。sync.RWMutex 是读写互斥锁,它允许多个 goroutine 同时进行读取操作(共享读锁),但在写入时需要独占锁(写锁)。对于读操作远多于写操作的场景,sync.RWMutex 通常能提供更好的并发性能。
示例代码:
Modoer 是一款以本地分享,多功能的点评网站管理系统。采用 PHP+MYSQL 开发设计,开放全部源代码。因具有非凡的访问速度和卓越的负载能力而深受国内外朋友的喜爱,不局限于商铺类点评,真正实现了多类型的点评,可以让您的网站点评任何事与物,同时增加产品模块,也更好的网站产品在网站上展示。Modoer点评系统 2.5 Build 20110710更新列表1.同步 旗舰版系统框架2.增加 限制图片
0
package main
import (
"fmt"
"sync"
"time"
)
// SafeMap 是一个并发安全的 map 包装器
type SafeMap struct {
mu sync.RWMutex // 使用读写锁,读操作并行,写操作互斥
data map[string]interface{}
}
// NewSafeMap 创建一个新的并发安全的 SafeMap 实例
func NewSafeMap() *SafeMap {
return &SafeMap{
data: make(map[string]interface{}),
}
}
// Set 设置键值对,加写锁保护
func (sm *SafeMap) Set(key string, value interface{}) {
sm.mu.Lock() // 获取写锁
defer sm.mu.Unlock() // 确保锁在函数返回时释放
sm.data[key] = value
}
// Get 获取键对应的值,加读锁保护
func (sm *SafeMap) Get(key string) (interface{}, bool) {
sm.mu.RLock() // 获取读锁
defer sm.mu.RUnlock() // 确保锁在函数返回时释放
val, ok := sm.data[key]
return val, ok
}
// Delete 删除键,加写锁保护
func (sm *SafeMap) Delete(key string) {
sm.mu.Lock() // 获取写锁
defer sm.mu.Unlock()
delete(sm.data, key)
}
// Len 获取 map 长度,加读锁保护
func (sm *SafeMap) Len() int {
sm.mu.RLock() // 获取读锁
defer sm.mu.RUnlock()
return len(sm.data)
}
func main() {
safeMap := NewSafeMap()
var wg sync.WaitGroup
// 模拟多个 goroutine 并发写入
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := fmt.Sprintf("key%d", i)
value := fmt.Sprintf("value%d", i)
safeMap.Set(key, value)
// fmt.Printf("Set: %s = %s\n", key, value) // 打印过多可能影响观察
}(i)
}
// 模拟多个 goroutine 并发读取
for i := 0; i < 50; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := fmt.Sprintf("key%d", i*2) // 尝试读取一些可能存在的键
val, ok := safeMap.Get(key)
if ok {
// fmt.Printf("Get: %s = %v\n", key, val)
} else {
// fmt.Printf("Get: %s not found\n", key)
}
}(i)
}
wg.Wait() // 等待所有 goroutine 完成
fmt.Printf("最终 map 长度: %d\n", safeMap.Len())
// 演示删除操作
safeMap.Delete("key10")
_, ok := safeMap.Get("key10")
fmt.Printf("删除 key10 后,key10 是否存在: %t\n", ok)
}适用场景:
Go 语言鼓励通过 channel 来进行 goroutine 之间的通信和数据同步,而不是直接共享内存。这种方式通常用于更复杂的并发模式,其中 map 的管理被封装在一个单独的 goroutine 中。
原理: 创建一个专门的 goroutine 来“拥有”并管理 map。其他 goroutine 不直接访问 map,而是通过 channel 向这个管理 goroutine 发送操作请求(如读、写、删除等),并从 channel 接收操作结果。这种模式将 map 的所有权和操作逻辑集中在一个地方,天然避免了数据竞争。
示例代码:
package main
import (
"fmt"
"sync"
"time"
)
// MapOperation 定义 map 操作类型
type MapOperation int
const (
OpSet MapOperation = iota // 设置键值对
OpGet // 获取键值
OpDelete // 删除键
OpLen // 获取长度
OpQuit // 退出管理器
)
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号