第二十三:内存逃逸、互斥锁 Map 线程安全
常见的内存逃逸 ?
setNx 是一个原子操作命令
golang中三种线程安全的MAP-CSDN博客
使用Go语言实现线程安全的Map_go map线程安全-CSDN博客
https://zhuanlan.zhihu.com/p/9651365604 // 处理map 并发问题
使用sync.Mutex
sync.Mutex
是一个互斥锁,可以保护一个临界区,确保同时只有一个goroutine可以访问这段代码。
package main
import (
"fmt"
"sync"
)
type SafeMap struct {
m map[string]int
lock sync.Mutex
}
func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]int),
}
}
func (sm *SafeMap) Set(key string, value int) {
sm.lock.Lock() // 在修改map之前加锁
sm.m[key] = value
sm.lock.Unlock() // 修改完成后解锁
}
func (sm *SafeMap) Get(key string) (int, bool) {
sm.lock.Lock() // 在读取map之前加锁
value, exists := sm.m[key]
sm.lock.Unlock() // 读取完成后解锁
return value, exists
}
func main() {
sm := NewSafeMap()
sm.Set("foo", 1)
value, exists := sm.Get("foo")
if exists {
fmt.Println("Value:", value)
} else {
fmt.Println("Key not found")
}
}
使用sync.RWMutex
sync.RWMutex
是一个读写锁,允许多个goroutine同时读取map,但写入时需要独占访问。这通常在读取操作远多于写入操作时更为高效。
package main
import (
"fmt"
"sync"
)
type SafeMap struct {
m map[string]int
lock sync.RWMutex
}
func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]int),
}
}
func (sm *SafeMap) Set(key string, value int) {
sm.lock.Lock() // 在修改map之前加锁(写锁)
sm.m[key] = value
sm.lock.Unlock() // 修改完成后解锁(写锁)
}
func (sm *SafeMap) Get(key string) (int, bool) {
sm.lock.RLock() // 在读取map之前加读锁
value, exists := sm.m[key]
sm.lock.RUnlock() // 读取完成后解锁(读锁)
return value, exists
}
func main() {
sm := NewSafeMap()
sm.Set("foo", 1)
value, exists := sm.Get("foo")
if exists {
fmt.Println("Value:", value)
} else {
fmt.Println("Key not found")
}
}
注意事项:性能和选择使用场景
-
使用互斥锁(
sync.Mutex
):适用于读和写操作大致平衡的场景。每次读或写操作都需要获取锁,可能会造成性能瓶颈。 -
使用读写锁(
sync.RWMutex
):适用于读操作远多于写操作的场景。读操作可以并行进行,而写操作则需要独占访问。这通常能提供更好的性能。但也要注意,频繁的读操作也可能因为频繁的锁获取和释放而影响性能。因此,选择哪种锁取决于你的具体应用场景。 例如,如果你的应用主要是查询操作,那么使用读写锁通常更合适。如果你的应用主要是更新操作,那么使用互斥锁可能更合适。 总之,选择合适的工具并根据实际使用场景进行优化是非常重要的。
channel 处理并发问题:
// 这个是错误的:没有起到并发作用
/**
* 并发编程,map的线程安全性问题,使用互斥锁的方式
*/
package main
import (
"sync"
"time"
"fmt"
)
var data map[int]int = make(map[int]int)
var wgMap sync.WaitGroup = sync.WaitGroup{}
var muMap sync.Mutex = sync.Mutex{}
func main() {
// 并发启动的协程数量
max := 10000
wgMap.Add(max)
time1 := time.Now().UnixNano()
for i := 0; i < max; i++ {
go modifySafe(i)
}
wgMap.Wait()
time2 := time.Now().UnixNano()
fmt.Printf("data len=%d, time=%d", len(data), (time2-time1)/1000000)
}
// 线程安全的方法,增加了互斥锁
func modifySafe(i int) {
//muMap.Lock()
data[i] = i
//muMap.Unlock()
wgMap.Done()
}
/**
* 并发编程,map的线程安全性问题,使用channel的方式
*/
package main
import (
"time"
"fmt"
)
var dataCh map[int]int = make(map[int]int)
var chMap chan int = make(chan int)
func main() {
// 并发启动的协程数量
max := 10000
time1 := time.Now().UnixNano()
for i := 0; i < max; i++ {
go modifyByChan(i)
}
// 处理channel的服务
chanServ(max)
time2 := time.Now().UnixNano()
fmt.Printf("data len=%d, time=%d", len(dataCh), (time2-time1)/1000000)
}
func modifyByChan(i int) {
chMap <- i
}
// 专门处理chMap的服务程序
func chanServ(max int) {
for {
i := <- chMap
dataCh[i] = i
if len(dataCh) == max {
return
}
}
}