当前位置: 首页 > news >正文

第二十三:内存逃逸、互斥锁 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
      }
   }
}

http://www.dtcms.com/a/129901.html

相关文章:

  • Spring Boot 项目基于责任链模式实现复杂接口的解耦和动态编排!
  • Python生成器:解锁高效编程的新姿势
  • 搭建一个Spring Boot聚合项目
  • 苍穹外卖day03
  • Redis之缓存更新策略
  • 10-常见笔试题-mk
  • 破解 MCP 认证难题方法深入了解考试内容
  • [MySQL] 索引
  • 使用Apache POI实现Java操作Office文件:从Excel、Word到PPT模板写入
  • 码界奇缘 Java 觉醒 后记 第二十二章 Epsilon无为秘境 - 寂静之地的内存试炼
  • 25软考中级*高项网课+历年真题+笔记+电子书+刷题【计算机软考】
  • C++——继承、权限对继承的影响
  • ubuntu学习day1
  • RuoYi-Vue升级为https访问-后端安装SSL证书(单台Linux服务器部署)
  • 图论基础理论
  • 低资源需求的大模型训练项目---调研0.5B大语言模型
  • 2025.04.13【Density 2d】| 基因表达数据可视化
  • Linux编程c/c++程序
  • 前端vue 项目px转为rem的自适应解决方案
  • open harmony多模组子系统分析
  • BM25、BGE以及text2vec-base-chinese的区别
  • [dp8_子数组] 乘积为正数的最长子数组长度 | 等差数列划分 | 最长湍流子数组
  • UE5角色状态机中跳跃落地移动衔接问题
  • markdown导出PDF,PDF生成目录
  • goc知识点
  • Symbol
  • C++学习之路,从0到精通的征途:string类的模拟实现
  • 操作系统基础:06 操作系统历史
  • C++ CUDA开发入门
  • VectorBT量化入门系列:第六章 VectorBT实战案例:机器学习预测策略