Go语言并发编程 ------ 锁机制详解

Go语言提供了丰富的同步原语来处理并发编程中的共享资源访问问题。其中最基础也最常用的就是互斥锁(Mutex)和读写锁(RWMutex)。

1. sync.Mutex(互斥锁)

Mutex核心特性

  • 互斥性/排他性同一时刻只有一个goroutine能持有锁
  • 不可重入:同一个goroutine重复加锁会导致死锁
  • 零值可用sync.Mutex的零值就是未锁定的互斥锁
  • 非公平锁:不保证goroutine获取锁的顺序

Mutex例子

例1:

package mainimport ("fmt""math/rand""sync""time"
)var wait sync.WaitGroup
var count = 0var lock sync.Mutexfunc main() {wait.Add(10)for i := 0; i < 10; i++ {go func(data *int) {// 加锁lock.Lock()// 模拟访问耗时time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))// 访问数据temp := *data// 模拟计算耗时time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))ans := 1// 修改数据*data = temp + ans// 解锁lock.Unlock()fmt.Println(*data)wait.Done()}(&count)}wait.Wait()fmt.Println("最终结果", count)
}

输出:

1
2
3
4
5
6
7
8
9
10
最终结果 10

解读:

  • lock 是一个互斥锁,用于确保在任何时刻只有一个 goroutine 可以访问和修改 count 变量,防止数据竞争。
  • 每个 goroutine 首先通过 lock.Lock() 加锁,确保在同一时间只有一个 goroutine 可以修改 count。
  • time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000))) 模拟了对数据的访问和计算耗时,这里的随机数生成器用于在每次循环中生成一个 0 到 999 之间的随机整数,作为睡眠的时间
  • wait.Wait() 阻塞主 goroutine,直到等待组中的所有 goroutine 都完成任务。
  • fmt.Println("最终结果", count) 打印 count 的最终值。

在 Go 语言中,func(data *int) 这样的写法是用来定义一个匿名函数,并且该匿名函数接受一个参数,参数类型是指向整型的指针。在这段代码的目的是在并发环境中对一个共享变量 count 进行修改,以避免数据竞争。

  • go func(data *int) { ... }(&count) 这里的 go 关键字用于启动一个新的 goroutine。
  • func(data *int) { ... } 是一个匿名函数,它接受一个参数 data,这个参数是一个指向整型的指针。
  • (&count) 表示传递给匿名函数的参数是 count 变量的地址。通过传递指针,匿名函数可以直接访问和修改 count 的值。

例2

package mainimport ("fmt""sync""time"
)var (counter intlock    sync.Mutex
)func main() {var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go increment(&wg)}wg.Wait()fmt.Println("Final counter:", counter)
}func increment(wg *sync.WaitGroup) {defer wg.Done()lock.Lock()         // 加锁defer lock.Unlock() // 使用defer确保解锁// 临界区temp := countertime.Sleep(1 * time.Millisecond)counter = temp + 1
}

输出:

Final counter: 10

解读:

  • var wg sync.WaitGroup:声明了一个等待组wg,用于等待所有goroutine完成。
  • wg.Add(1):为每次循环增加一个等待计数。
  • go increment(&wg):启动goroutine运行increment函数,并传入等待组的地址。
  • wg.Wait():等待所有等待计数为零,即所有goroutine完成。
  • defer wg.Done():使用defer关键字确保函数执行完毕后调用wg.Done(),减少等待组的一个计数。
  • lock.Lock():在函数执行前加锁,防止多个goroutine同时访问counter。
  • defer lock.Unlock():同样使用defer关键字确保函数执行完毕后解锁。
  • 临界区代码段:将counter的值赋给temp,休眠1毫秒,然后将counter设置为temp + 1。这里通过休眠模拟了一个耗时操作。

2. sync.RWMutex(读写锁)

Go 中读写互斥锁的实现是 sync.RWMutex,它也同样实现了 Locker 接口,但它提供了更多可用的方法,如下:

// 加读锁
func (rw *RWMutex) RLock()// 非阻塞地尝试加读锁 (Go 1.18+)
func (rw *RWMutex) TryRLock() bool// 解读锁
func (rw *RWMutex) RUnlock()// 加写锁
func (rw *RWMutex) Lock()// 非阻塞地尝试加写锁 (Go 1.18+)
func (rw *RWMutex) TryLock() bool// 解写锁
func (rw *RWMutex) Unlock()

1. RWMutex基本概念

读写锁的特点

  • 并发读:多个goroutine可以同时持有读锁
  • 互斥写:写锁是排他的,同一时间只能有一个goroutine持有写锁
  • 写优先:当有写锁等待时,新的读锁请求会被阻塞,防止写锁饥饿

Mutex的区别

特性MutexRWMutex
并发读不支持支持多个goroutine同时读
并发写不支持不支持
性能一般读多写少场景性能更好
复杂度简单相对复杂

2. RWMutex的工作原理

锁状态

  • 当写锁被持有时:所有读锁和写锁请求都会被阻塞
  • 当读锁被持有时:新的读锁请求可以立即获得锁,写锁请求会被阻塞
  • 当写锁请求等待时:新的读锁请求会被阻塞(写优先)

内部实现要点

  1. 读者计数:记录当前持有读锁的goroutine数量
  2. 写者标记:标识是否有goroutine持有或等待写锁
  3. 写者信号量:用于唤醒等待的写者
  4. 读者信号量:用于唤醒等待的读者

3. RWMutex的例子

线程安全的缓存实现

type Cache struct {mu    sync.RWMutexitems map[string]interface{}
}func (c *Cache) Get(key string) (interface{}, bool) {c.mu.RLock()defer c.mu.RUnlock()item, found := c.items[key]return item, found
}func (c *Cache) Set(key string, value interface{}) {c.mu.Lock()defer c.mu.Unlock()c.items[key] = value
}func (c *Cache) Delete(key string) {c.mu.Lock()defer c.mu.Unlock()delete(c.items, key)
}

解读

Cache 结构体

  • items:一个映射(map),键为字符串,值为接口类型(interface{}),用于存储缓存数据。
  • mu:一个sync.RWMutex实例,用于控制对items的并发访问。

Get 方法:

  • c.mu.RLock():获取读锁,允许多个读协程同时访问items。
  • defer c.mu.RUnlock():确保在函数返回前释放读锁
  • item, found := c.items[key]:从items中获取指定key对应的值,并判断该key是否存在。
  • return item, found:返回获取的值和是否找到的布尔值。

Set 方法:

  • c.mu.Lock():获取写锁,确保只有一个写协程可以访问items。
  • defer c.mu.Unlock():确保在函数返回前释放写锁
  • c.items[key] = value:将指定key对应的值设置为value。

Delete 方法:

  • c.mu.Lock():获取写锁,确保只有一个写协程可以访问items。
  • defer c.mu.Unlock():确保在函数返回前释放写锁。
  • delete(c.items, key):从items中删除指定key对应的键值对。

3.互斥锁和读写锁的区别和应用场景

核心区别对比

特性互斥锁(Mutex)读写锁(RWMutex)
并发读完全互斥,读操作也需要独占锁允许多个goroutine同时持有读锁
并发写互斥,同一时间只有一个写操作互斥,同一时间只有一个写操作
锁类型单一锁类型区分读锁(RLock)和写锁(Lock)
性能开销较高(所有操作都互斥)读操作开销低,写操作开销与Mutex相当
实现复杂度简单相对复杂
适用场景读写操作频率相当或写多读少读操作远多于写操作的场景

选择场景

  1. 优先考虑RWMutex当

    • 读操作次数是写操作的5倍以上
    • 读操作临界区较大(耗时较长)
    • 需要支持高频并发读取
  2. 选择Mutex当

    • 读写操作频率相当(写操作占比超过20%)
    • 临界区非常小(几个CPU周期就能完成)
    • 代码简单性比极致性能更重要
    • 需要锁升级/降级(虽然Go不支持,但Mutex更不容易出错)
  3. 特殊考虑

    • 对于极高性能场景,可考虑atomic原子操作
    • 对于复杂场景,可考虑sync.Map或分片锁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/93654.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/93654.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8月17日星期天今日早报简报微语报早读

8月17日星期天&#xff0c;农历闰六月廿四&#xff0c;早报#微语早读。1、《南京照相馆》领跑&#xff0c;2025年暑期档电影总票房破95亿&#xff1b;2、神舟二十号圆满完成第三次出舱任务&#xff1b;3、宇树G1人形机器人100米障碍赛再夺金牌&#xff1b;4、广东佛山新增报告基…

在QML中使用Chart组件

目录前言1. 如何安装 Chart 组件2. 创建 QML 工程时的常见问题3. 解决方案&#xff1a;改用 QApplication QQuickView修改主函数&#xff08;main.cpp&#xff09;4. QApplication 与 QGuiApplication 的差异为什么 Qt Charts 需要 QApplication&#xff1f;总结示例下载前言 …

【P40 6-3】OpenCV Python——图像融合(两张相同属性的图片按比例叠加),addWeighted()

P40 6-3 文章目录import cv2 import numpy as npback cv2.imread(./back.jpeg) smallcat cv2.imread(./smallcat1.jpeg)#只有两张图的属性是一样的才可以进行溶合 print(back.shape) print(smallcat.shape)result cv2.addWeighted(smallcat, 0.7, back, 0.3, 0) cv2.imshow(…

传输层协议 TCP(1)

传输层协议 TCP&#xff08;1&#xff09; TCP 协议 TCP 全称为 “传输控制协议(Transmission Control Protocol”). 人如其名, 要对数据的传输进行一个详细的控制; TCP 协议段格式 • 源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去; • 32 位序号/32 位确认号: 后面详…

黎阳之光:以动态感知与 AI 深度赋能,引领电力智慧化转型新革命

当全球能源结构加速向清洁低碳转型&#xff0c;新型电力系统建设成为国家战略核心&#xff0c;电力行业正经历从传统运维向智慧化管理的深刻变革。2024 年《加快构建新型电力系统行动方案》明确提出&#xff0c;到 2027 年需建成全国智慧调度体系&#xff0c;实现新能源消纳率突…

自动驾驶中的传感器技术34——Lidar(9)

补盲lidar设计&#xff1a;机械式和半固态这里不再讨论&#xff0c;这里主要针对全固态补盲Lidar进行讨论1、系统架构设计采用Flash方案&#xff0c; 设计目标10m10%&#xff0c;实现30m距离的点云覆盖&#xff0c;同时可以验证不同FOV镜头的设计下&#xff0c;组合为多款产品。…

Originality AI:原创度和AI内容检测工具

本文转载自&#xff1a;Originality AI&#xff1a;原创度和AI内容检测工具 - Hello123工具导航 ** 一、AI 内容诚信管理专家 Originality AI 是面向内容创作者的全栈式质量检测平台&#xff0c;整合 AI 内容识别、抄袭查验、事实核查与可读性分析四大核心功能&#xff0c;为…

OpenCV图像平滑处理方法详解

引言 在数字图像处理中&#xff0c;图像平滑是一项基础而重要的预处理技术。它主要用于消除图像中的噪声、减少细节层次&#xff0c;为后续的图像分析&#xff08;如边缘检测、目标识别等&#xff09;创造更好的条件。OpenCV作为最流行的计算机视觉库之一&#xff0c;提供了多种…

每天两道算法题:DAY1

题目一&#xff1a;金币 题目一&#xff1a;金币 1.题目来源&#xff1a; NOIP2015 普及组 T1&#xff0c;难度红色&#xff0c;入门签到题。 2.题目描述&#xff1a; 3.题目解析&#xff1a; 问题转化&#xff1a;求下面的一个数组的前 k 项和。 4.算法原理&#xff1a; …

C++核心语言元素与构建块全解析:从语法规范到高效设计

&#x1f4cc; 为什么需要双维度学习C&#xff1f;核心语言元素 → 掌握标准语法规则&#xff08;避免未定义行为Undefined behavior&#xff09;构建块&#xff08;Building Blocks&#xff09; → 像搭积木一样组合功能&#xff08;提升工程能力&#xff09; 例如&#xff1a…

RK3588开发板Ubuntu系统烧录

Ubuntu22.04——YOLOv8模型训练到RK3588设备部署和推理 文章中给出了通过ARM设备上面的NPU进行深度学习的模型推理过程,在此之前,我们在收到一块全新的rk3588开发板后,需要对其进行系统的烧录,这里以Ubuntu22.04系统为例。 目录 1.获取待烧录系统的镜像 2.烧录工具准备 2.1…

AI评测的科学之道:当Benchmark遇上统计学

AI评测的科学之道&#xff1a;当Benchmark遇上统计学 —— 如何客观评估大模型能力&#xff0c;避免落入数据陷阱 在人工智能尤其是大语言模型&#xff08;LLU&#xff09;爆发式发展的今天&#xff0c;各类模型榜单&#xff08;如Open LLM Leaderboard、LMSys Arena&#xff0…

CSS 基础入门教程:从零开始学习样式表

一、CSS 简介CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09;是一种用于描述 HTML 或 XML 等文档呈现方式的语言。它是现代网页设计的三大核心技术之一&#xff0c;与HTML&#xff08;结构层&#xff09;和JavaScript&#xff08;行为层&#xff09;…

图解简单选择排序C语言实现

1 简单选择排序 简单选择排序&#xff08;Simple Selection Sort&#xff09;是一种基础且直观的排序算法&#xff0c;其核心思想是通过重复选择未排序部分中的最小&#xff08;或最大&#xff09;元素&#xff0c;并将其放到已排序部分的末尾&#xff0c;逐步完成整个序列的排…

FPS游戏时,你的电脑都在干什么(CS2)

人物介绍&#xff1a;CPU > 你忠实的处理器 i5-13600KFGPU > 你花大价钱买的显卡 RTX3060&#xff08;不是自己的配置&#xff0c;自己的是XEON E5GTX1060&#xff0c;测不出来&#xff0c;上面是社区一个好心大哥的数据&#xff0c;较为精准&#xff09;&#…

MySQL完整重置密码流程(针对 macOS)

MySQL完整重置密码流程&#xff08;针对 macOS&#xff09; 1. 强制停止 MySQL 服务 sudo /usr/local/mysql/support-files/mysql.server stop sudo killall mysqld mysqld_safe # 确保所有进程停止2. 以安全模式启动&#xff08;跳过权限验证&#xff09; sudo /usr/local/my…

Python数据类型转换详解:从基础到实践

在Python编程中&#xff0c;数据类型转换是一项基础且频繁使用的操作。无论是处理用户输入、进行数值计算还是数据处理&#xff0c;都离不开类型转换。本文将系统介绍Python中的数据类型体系&#xff0c;详解类型转换的规则与实践技巧&#xff0c;帮助你在实际开发中灵活运用。…

智能制造——解读车企数字化转型构建高效经营管理数据治理体系【附全文阅读】

适应人群为车企数字化转型决策者、数据管理负责人、IT 部门从业者、财务及业务部门管理者。主要内容围绕车企数字化转型中经营管理数据治理体系构建展开,核心包括诊断背景(以经营管理数字化为切入点,聚焦财务业务在线化、零点月结等痛点,应对系统与数据问题);现状诊断(从…

STM32的UART奇偶校验注意

关键点&#xff1a;设置为9位数据位&#xff0c; STM32的UART奇偶校验注意_stm32串口奇校验初始化程序-CSDN博客https://blog.csdn.net/JacobFang/article/details/118993643 特此记录 anlog 2025年8月13日

Origin绘制正态分布直方图+累积概率图|科研论文图表教程(附数据格式模板)

免费查看完整教程(包括数据格式) ↑ ↑ ↑ 目录 本 期 导 读 No.1 理解图形 1 定义 2 图形特点 3 应用场景 No.2 画图教程 1 导入数据,绘制图形 2 设置绘图细节 本 期 导 读 直方图,以柱状高低直观展现各区间数据的分布密度,集中趋势、离散程度与异常…