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

go语言并发编程-超详细mutex解析

文章目录

  • 1 go语言并发编程学习-mutex
    • 1.1 学习过程
    • 1.2 如何解决资源并发访问的问题?【基本用法】
      • 1.2.1 并发访问带来的问题
        • 1.2.1.1 导致问题的原因
      • 1.2.2 race detector检查data race
      • 1.2.3 mutex的基本实现机制以及使用方法
        • 1.2.3.1 具体使用-1
        • 1.2.3.1 具体使用-2

1 go语言并发编程学习-mutex

1.1 学习过程

在这里插入图片描述

1.2 如何解决资源并发访问的问题?【基本用法】

本小节主要为了解答以下问题:

  1. 为什么需要解决并发访问的问题?
  2. 怎么通过race detector来查找程序中的data race?
  3. mutex的基本机制和基本使用方法?

1.2.1 并发访问带来的问题

1. 多个goroutine并发更新计数器

在多个goroutine的情况下并发更新计数器,得到的值可能不符合预期。

package mainimport ("fmt""sync"
)var counter int
var wg sync.WaitGroupfunc increment() {defer wg.Done()counter += 100
}func main() {wg.Add(1000) // 这个可以先不管,理解为,main函数需要等待goroutine都执行完才能退出,可以把wg相关全去掉,在main函数后面加上time.sleep(time.second * 10)for i := 0; i < 1000; i++ {go increment()}wg.Wait()fmt.Println("Final counter:", counter) // 期望输出 2,但可能输出 0 或 1
}

2. 更新用户的账户余额

在用户收入和支出的时候,如果不同的goroutine同时对该账户余额进行更新处理的时候,可能会导致余额错误

package mainimport ("fmt""sync"
)var balance int = 1000
var wg sync.WaitGroupfunc deposit(amount int) {defer wg.Done()balance += amount
}func withdraw(amount int) {defer wg.Done()balance -= amount
}func main() {wg.Add(2000)for i := 0; i < 1000; i++ {go deposit(1)go withdraw(1)}wg.Wait()fmt.Println("Final balance:", balance) // 期望输出 1000,但可能输出其他值
}

3. 秒杀系统

没有互斥锁的情况下,可能会出现超卖的情况。也就是商品已经没有了,但是还是可以进行出售商品,商品数量减1的操作。

package mainimport ("fmt""sync"
)var stock int = 10
var wg sync.WaitGroupfunc purchase() {defer wg.Done()if stock > 0 {fmt.Println("Stock:", stock)stock--}
}func main() {wg.Add(100000)for i := 0; i < 100000; i++ {go purchase()}wg.Wait()fmt.Println("Final stock:", stock) // 期望输出 0,但可能输出负数
}

还有一些其他的场景,比如并发写入buffer等等,不解决并发访问的问题,就会发生很严重的后果。

1.2.1.1 导致问题的原因

并发访问问题的核心在于对共享资源的非原子性操作。临界区是指一段需要独占访问的代码块,多个goroutine在执行这段代码时,如果没有同步机制(如互斥锁)来保证互斥访问,就可能会产生数据竞争,导致数据不一致和其他问题。以下从临界区的角度来解释这些问题。下面分析多个goroutine并发更新计数器:

计数器的更新操作通常包括以下步骤:

  1. 读取当前计数器的值
  2. 对读取的值进行加法运算
  3. 将结果写回计数器

在并发情况下,如果两个goroutine同时执行这三个步骤中的任意一个步骤,没有同步机制来保证这三个步骤是原子操作,就会产生问题:

Goroutine 1: 读取 counter = 0
Goroutine 2: 读取 counter = 0
Goroutine 1: counter = 0 + 1 => counter = 1
Goroutine 2: counter = 0 + 1 => counter = 1 (覆盖了Goroutine 1的结果)

那么怎么在程序运行的时候发现呢?可以参考一下race detector工具

1.2.2 race detector检查data race

可以使用上文的秒杀系统作为例子。写这个的时候,图片转存失败,因此决定用极客上的图片。
1、 在执行go run counter.go的时候会出现以下结果,是可以正常运行通过的,但是结果不如愿:
在这里插入图片描述
2、但是假如race之后:go run -race main.go
在这里插入图片描述
这个警告不但会告诉你有并发问题,而且还会告诉你哪个goroutiine在哪一行对哪个变量有写操作,同时,哪个goroutine在哪一行对哪个变量有读操作,就是这些并发的读写访问,引起了datarace。
例子中的goroutine 10 对内存地址0x000126010有读的操作(ctounter.go文件第16行),同时,goroutine7对内存地址0x00c000126010有写的操作(counter.go文件第16行)。而且还可能有多个goroutine在同时进行读写,所以,警告信息可能会很长。

总结一下,通过在编译的时候插入一些指令,在运行时通过这些插入的指令检测并发读写从而发现data race问题,就是这个工具的实现机制。
既然这个例子存在data race问题,我们就要想办法来解决它。这个时候,我们这节课的主角Mutex就要登场了,它可以轻松地消除掉data race。
具体怎么做呢?下面,我就结合这个例子,来具体说一说Mutex的基本用法。

1.2.3 mutex的基本实现机制以及使用方法

在这里插入图片描述
​ mutex的基本实现的机制,就是每次只允许一个goroutine进入临界区,具体就是进入临界区的时候给临界区加上一个锁,禁止其他goroutine进入临界区,在退出临界区的时候释放锁,从而允许其他goroutine进入。

Mutex 是 Go 语言中常用的同步原语,用于控制对共享资源的独占访问。Mutex 实现了 sync.Locker 接口,该接口定义了两个方法:LockUnlock。在解释 Mutex 的基本使用方法之前,先简单介绍一下 sync.Locker 接口:

type Locker interface {    Lock()    Unlock() 
} 

任何实现了 LockUnlock 方法的类型,都可以被视为 Locker,所以 sync.Mutex 也实现了这个接口。以下是一个基本的 sync.Mutex 的使用方法:

1、基本使用方法:

声明一个sync.Mutex类型的变量,无需初始化

var mutex sync.Mutex 

2、在需要保护的临界区前调用 Lock 方法:

mutex.Lock() 

3、在临界区结束后调用 Unlock 方法:

mutex.Unlock() 
1.2.3.1 具体使用-1

这种使用,主要是在临界区代码中直接使用mutex即可。

package mainimport ("fmt""sync"
)var stock int = 10
var wg sync.WaitGroup
var mutex sync.Mutexfunc purchase() {defer wg.Done()mutex.Lock()         // 加锁,进入临界区defer mutex.Unlock() // 确保解锁if stock > 0 {fmt.Println("Stock:", stock)stock--}
}func main() {wg.Add(100000)for i := 0; i < 100000; i++ {go purchase()}wg.Wait()fmt.Println("Final stock:", stock) // 期望输出 0,但可能输出负数
}
1.2.3.1 具体使用-2

该使用是把mutex和临界区资源封装为一个类,这样更好的进行复用,不暴露内部实现

package mainimport ("fmt""sync"
)// StockManager 结构体封装了库存和互斥锁
type StockManager struct {stock intmutex sync.Mutex
}// NewStockManager 创建一个新的 StockManager
func NewStockManager(initialStock int) *StockManager {return &StockManager{stock: initialStock}
}// Purchase 尝试购买一个商品
func (sm *StockManager) Purchase() bool {sm.mutex.Lock()defer sm.mutex.Unlock()if sm.stock > 0 {sm.stock--fmt.Println("Purchase successful, remaining stock:", sm.stock)return true}fmt.Println("Purchase failed, out of stock")return false
}// GetStock 获取当前库存
func (sm *StockManager) GetStock() int {sm.mutex.Lock()defer sm.mutex.Unlock()return sm.stock
}func main() {sm := NewStockManager(10)var wg sync.WaitGroupnumUsers := 100000wg.Add(numUsers)for i := 0; i < numUsers; i++ {go func() {defer wg.Done()sm.Purchase()}()}wg.Wait()fmt.Println("Final stock:", sm.GetStock())
}

下一篇:mutex的原理以及常见的错误。

相关文章:

go语言并发编程-超详细mutex解析

文章目录 1 go语言并发编程学习-mutex1.1 学习过程1.2 如何解决资源并发访问的问题&#xff1f;【基本用法】1.2.1 并发访问带来的问题1.2.1.1 导致问题的原因 1.2.2 race detector检查data race1.2.3 mutex的基本实现机制以及使用方法1.2.3.1 具体使用-11.2.3.1 具体使用-2 1 …...

VirtualBox Debian 自动安装脚本

概览 相较于原脚本&#xff08;安装目录/UnattendedTemplates/debian_pressed.cfg&#xff09;更新如下内容&#xff1a; 配置清华镜像源配置仅主机网卡&#xff08;后续只需添加仅主机网卡即可&#xff09;配置Root用户远程登录配置用户sudo组 脚本 debian_pressed.cfg ##…...

最好的开放式耳机?五款红榜开放式耳机推荐!

面对众多的开放式耳机选项&#xff0c;消费者可能会感到难以抉择。买耳机不一定要买最贵最好的&#xff0c;但是一定要选最适合自己的&#xff0c;为了使选择过程更加容易&#xff0c;我提供了一些建议&#xff0c;推荐了几款既适合日常使用又佩戴舒适的热门开放式耳机。 开放式…...

线性代数之线性方程组

目录 线性方程组 1. 解的个数 齐次线性方程组&#xff1a; 非齐次线性方程组&#xff1a; 2. 齐次线性方程组的解 3. 非齐次线性方程组的解 4. 使用 Python 和 NumPy 求解线性方程组 示例代码 齐次线性方程组 非齐次线性方程组 示例结果 齐次线性方程组 非齐次线性…...

速盾:怎么查看是否使用cdn服务?

CDN&#xff08;Content Delivery Network&#xff09;&#xff0c;即内容分发网络&#xff0c;是一种加速网络内容传输的技术。通过在全球各地建立分布式的节点服务器&#xff0c;将网站的静态资源缓存到最近的节点服务器上&#xff0c;使用户可以从离自己地理位置最近的节点服…...

828华为云征文|采用Flexus云服务器X实例部署RTSP直播服务器

一、前言 这篇文章讲解&#xff1a; 采用华为云最新推出的Flexus云服务器X实例搭建RTSP服务器&#xff0c;完成视频直播需求。 随着实时视频流传输需求的增长&#xff0c;RTSP&#xff08;实时流协议&#xff09;服务器成为了许多视频监控、直播和多媒体应用的核心组件。在当…...

Spring Cloud Gateway(二)

Spring Cloud Gateway&#xff08;二&#xff09; 文章目录 Spring Cloud Gateway&#xff08;二&#xff09;Gateway工作原理为什么使用API网关高并发Gateway性能优化 Gateway工作原理 Spring Cloud Gateway旨在为微服务架构提供简单、有效并且统一的API路由管理方式。它不仅…...

docker 简易入门

# docker 简易入门 docker由几个组成部分 docker client: 即 docker 命令行工具 docker host: 宿主机&#xff0c;docker daemon 的运行环境服务器 docker daemon: docker 的守护进程&#xff0c;docker client 通过命令行与 docker daemon 交互 container: 最小型的一个操…...

【看雪-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…...

记录一个前端学习小组的收集的模版

问题1&#xff1a;输入“您的姓名”&#xff0c;选择“短答案”作为问题类型。问题2&#xff1a;输入“您是否愿意继续参加前端学习小组&#xff1f;”&#xff0c;选择“单选”作为问题类型&#xff0c;并添加选项“是”和“否”。问题3&#xff1a;输入“如果您选择‘是’&am…...

Rk3588 Android12 AIDL 开发

AIDL (Android Interface Definition Language) 和 HIDL (HAL Interface Definition Language) 都是 Android 系统中用于定义接口的工具&#xff0c;但它们有不同的用途和特性。 AIDL (Android Interface Definition Language) 用途&#xff1a; 主要用于应用程序之间的进程间…...

两个长整数字符串求和(不允许使用ES6+)

两个长整数字符串求和(不允许使用ES6), 面试手撸代码遇到到这个问题 1. 实现方式第一种 // 短整数字符串前边补 0; num需要补 0 的短整数字符串, len 长整数字符串的长度 function fillZero (num, len) {let str num.toString();if (str.length < len) {str 0.repeat(…...

11 Java 方法引用、异常处理、Java接口之函数式编程(接口知识补充Function<T,R>、BiFunction<T, U, R>和自定义泛型接口)

文章目录 前言一、Java接口之函数式编程 --- 接口知识补充1 Function<T,R>泛型接口2 BiFunction<T, U, R>泛型接口3 自定义泛型函数式编程接口4 使用lambda表达式、方法引用进行函数式编程二、方法引用1 方法引用初体验(以Array.sort()方法为例)(1)什么是方法引…...

深入探索 Go 语言的编译器与垃圾回收机制

Go 编译器 Go 编译器是通过 go 工具执行的&#xff0c;这个工具的功能不仅仅是生成可执行文件。你可以使用 go tool compile 命令来编译一个 Go 源文件。这个操作将生成一个目标文件&#xff0c;也就是 .o 后缀的文件。以下是在 macOS Mojave 系统上执行的命令和结果展示&…...

2024国赛数学建模-模拟火算法(MATLAB 实现)

模拟退火算法 1.1 算法原理 模拟退火算法的基本思想是从一给定解开始 ,从邻域 中随机产生另一个解 ,接受 Metropolis准则允许目标函数在 有限范围内变坏 ,它由一控制参数 t决定 ,其作用类似于物 理过程中的温度 T,对于控制参数的每一取值 ,算法持续进 行“产生 —判断 —接受…...

YOLOv8 只检测人 只画框不要标签

参考了这个&#xff1a;YOLOv8只检测人&#xff08;或其他一种或者多种类别&#xff09;_yolov8只检测指定类别-CSDN博客 1. 只检测人&#xff1a;predict的时候指定参数classes[0] 2. 只画框不要标签&#xff1a;plot的时候传入labelsFalse 3. 标签中去掉置信度&#xff1a…...

如何将网络安全防范游戏化

组织对威胁的准备和恢复能力跟不上网络犯罪分子的进步。 一些首席执行官仍然认为网络安全需要偶尔干预&#xff0c;而不是持续关注。 但对于许多公司来说&#xff0c;情况并非如此&#xff1b;网络威胁准备需要协调一致的培训工作&#xff0c;因此网络安全团队在攻击发生时已…...

Qt QGraphicsView实现图片放缩、鼠标拖动移动、鼠标点位置放大缩小_图片查看

QtQGraphicsView实现图片放缩、鼠标拖动移动、鼠标点位置放大缩小 头文件&#xff1a; #ifndef TIMGWIDGET_H #define TIMGWIDGET_H#include <QGraphicsItem> #include <QMainWindow> #include <QObject> #include <QWidget>// class TImgWidget : pu…...

Percona Toolkit 神器全攻略(复制类)

Percona Toolkit 神器全攻略&#xff08;复制类&#xff09; Percona Toolkit 神器全攻略系列共八篇&#xff0c;前文回顾&#xff1a; 前文回顾Percona Toolkit 神器全攻略Percona Toolkit 神器全攻略&#xff08;实用类&#xff09;Percona Toolkit 神器全攻略&#xff08;配…...

SQLite3 数据类型深入全面讲解

SQLite3&#xff0c;作为一款轻量级的数据库管理系统&#xff0c;在数据存储方面展现出了其独特的魅力。它不仅支持标准的SQL语法&#xff0c;还提供了丰富的数据类型供开发者选择。这些数据类型不仅涵盖了基本的数值和文本类型&#xff0c;还包括了日期时间、二进制数据等复杂…...

ESP32无人机飞控C++工具库UAV_utils详解

1. UAV_utils 库概述UAV_utils 是一个面向无人机&#xff08;Unmanned Aerial Vehicle&#xff09;固件开发的轻量级 C 工具库&#xff0c;专为基于 ESP32 平台的飞控系统设计。其核心定位并非替代成熟飞控框架&#xff08;如 PX4 或 ArduPilot&#xff09;&#xff0c;而是为嵌…...

别再搞混了!天线近场和远场到底怎么分?用喇叭天线和对数周期天线实测告诉你

天线近场与远场划分的工程实践指南&#xff1a;从理论误区到实测解决方案 在微波暗室中调试天线时&#xff0c;工程师小王遇到了一个棘手问题&#xff1a;使用同一套测试设备&#xff0c;喇叭天线在18GHz频段的辐射方向图总是出现异常波动&#xff0c;而对数周期天线在2GHz频段…...

从办公室到车间:给IT网管的Profinet入门避坑指南(含VLAN与安全配置)

从办公室到车间&#xff1a;IT工程师的Profinet工业网络融合实战手册 当IT工程师第一次踏入嘈杂的工厂车间&#xff0c;面对那些闪烁着信号灯的PLC和伺服驱动器时&#xff0c;往往会感到一丝无所适从。这就像一位习惯在城市道路驾驶的司机&#xff0c;突然被要求操作一架喷气式…...

5个高效技巧:downkyi批量下载完全指南

5个高效技巧&#xff1a;downkyi批量下载完全指南 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xff09;。 项目…...

PyCharm虚拟环境配置避坑指南:为什么你的模块导入有提示但运行报错?

PyCharm虚拟环境配置避坑指南&#xff1a;为什么你的模块导入有提示但运行报错&#xff1f; 作为Python开发者&#xff0c;PyCharm的智能提示功能是我们日常开发的重要助力。但你是否遇到过这样的情况&#xff1a;明明在虚拟环境中安装了模块&#xff0c;代码运行时一切正常&am…...

【郑州大学主办 | SPIE出版社出版,ISSNISBN双刊号出版 | 通信技术、计算机视觉与算法、嵌入式系统技术、机器人领域EI】2026年机器学习与嵌入式系统国际学术会议(MLES 2026)

MLES 2026会议已成功申请到SPIE出版社出版&#xff01;ISSN&ISBN双刊号出版&#xff01; 2026年机器学习与嵌入式系统国际学术会议&#xff08;MLES 2026&#xff09; 2026 International Conference on Machine Learning and Embedded Systems 2026年4月24-26日 &a…...

终极C++编码标准指南:基于C++核心规范的AI驱动最佳实践

终极C编码标准指南&#xff1a;基于C核心规范的AI驱动最佳实践 【免费下载链接】everything-claude-code The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Curso…...

版图绘制汇总十四(PDK里有什么)

PDK--process design kit 有以下信息&#xff1a; 1、工艺库&#xff08;工艺厂提供的电路&#xff0c;版图设计基础信息&#xff0c;电路有spice器件模型和仿真数据模型model&#xff0c;器件描述格式CDF等。版图有pcell基础单元器件的版图。 2、IP库(工艺库已成型&#xff0c…...

4种突破数字内容壁垒的技术方案:面向研究者与创作者的开源工具指南

4种突破数字内容壁垒的技术方案&#xff1a;面向研究者与创作者的开源工具指南 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fa…...

让 DDL 源码真正可读:SAP ADT 里 DDL Formatter 的配置逻辑、团队协作方式与项目实践

在很多 ABAP 项目里,开发人员会把注意力集中在语义正确、激活成功、性能可接受这些层面,却容易低估一件事:DDL 源码的可读性,直接影响建模质量、代码审查效率,以及团队协作时的认知成本。CDS 语句一旦开始出现较长的元素列表、复杂的 Boolean expression、多层 JOIN、多个…...