Golang之路---04 并发编程——互斥锁和读写锁

news2025/1/20 3:55:21

互斥锁和读写锁

面对并发问题,我们始终应该优先考虑使用信道,如果通过信道解决不了的,不得不使用共享内存来实现并发编程的,那 Golang 中的锁机制,就是你绕不过的知识点了。
在 Golang 里有专门的方法来实现锁,还是上一节里介绍的 sync 包。

这个包有两个很重要的锁类型

一个叫 Mutex, 利用它可以实现互斥锁。

一个叫 RWMutex,利用它可以实现读写锁。

互斥锁 :Mutex

使用互斥锁(Mutex,全称 mutual exclusion)是为了来保护一个资源不会因为并发操作而引起冲突导致数据不准确。
举个例子,就像下面这段代码,我开启了三个协程,每个协程分别往 count 这个变量加1000次 1,理论上看,最终的 count 值应试为 3000


func add(count *int, wg *sync.WaitGroup){
    for i:= 0; i < 1000; i++{
        *count = *count + 1
    }
    wg.Done()
}

func main() {
    var wg sync.WaitGroup
    count := 0
    wg.Add(3)
    go add(&count, &wg)
    go add(&count, &wg)
    go add(&count, &wg)

    wg.Wait()
    /* 
    count的值为: 2078
    count的值为: 2516
    count的值为: 2079
    */
    fmt.Println("count的值为:",count)
}

可运行多次的结果,都不相同

   count的值为: 2078
   count的值为: 2516
   count的值为: 2079

原因就在于这三个协程在执行时,先读取 count 再更新 count 的值,而这个过程并不具备原子性,所以导致了数据的不准确。

解决这个问题的方法,就是给 add 这个函数加上 Mutex 互斥锁,要求同一时刻,仅能有一个协程能对 count 操作。

在写代码前,先了解一下 Mutex 锁的两种定义方法

// 第一种
var lock *sync.Mutex
lock = new(sync.Mutex)

// 第二种
lock := &sync.Mutex{}

利用互斥锁修改上面的代码,得:


func add(count *int, wg *sync.WaitGroup, lock *sync.Mutex){
    for i:= 0; i < 1000; i++{
        lock.Lock()
        *count = *count + 1
        lock.Unlock()
    }
    wg.Done()
}

func main() {
    var wg sync.WaitGroup
    lock := &sync.Mutex{}
    count := 0
    wg.Add(3)
    go add(&count, &wg, lock)
    go add(&count, &wg, lock)
    go add(&count, &wg, lock)

    wg.Wait()
    
    fmt.Println("count的值为:",count)
}

此时,不管你执行多少次,输出都只有一个结果

count 的值为: 3000

使用 Mutex 锁的注意事项:

  • 同一协程里,不要在尚未解锁时再次使加锁

  • 同一协程里,不要对已解锁的锁再次解锁

  • 加了锁后,别忘了解锁,必要时使用 defer 语句

读写锁:RWMutex

RWMutex,它将程序对资源的访问分为读操作和写操作
为了保证数据的安全,它规定了当有人还在读取数据(即读锁占用)时,不允计有人更新这个数据(即写锁会阻塞)

为了保证程序的效率,多个人(线程)读取数据(拥有读锁)时,互不影响不会造成阻塞,它不会像 Mutex 那样只允许有一个人(线程)读取同一个数据。

读锁允许多个线程同时获得,因为读操作本身是线程安全的。
而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的。
读写锁的特点是:读读不互斥、读写互斥、写写互斥

定义一个 RWMuteux 锁,有两种方法

// 第一种
var lock *sync.RWMutex
lock = new(sync.RWMutex)

// 第二种
lock := &sync.RWMutex{}

RWMutex 里提供了两种锁,每种锁分别对应两个方法,为了避免死锁,两个方法应成对出现,必要时请使用 defer。

读锁:调用 RLock 方法开启锁,调用 RUnlock 释放锁

写锁:调用 Lock 方法开启锁,调用 Unlock 释放锁(和 Mutex类似)

基本遵循原则:

  • 写锁定情况下,对读写锁进行读锁定或者写锁定,都将阻塞;而且读锁与写锁之间是互斥的;

  • 读锁定情况下,对读写锁进行写锁定,将阻塞;加读锁时不会阻塞;

  • 对未被写锁定的读写锁进行写解锁,会引发 Panic;

  • 对未被读锁定的读写锁进行读解锁的时候也会引发 Panic;

  • 写解锁在进行的同时会试图唤醒所有因进行读锁定而被阻塞的 goroutine;

  • 读解锁在进行的时候则会试图唤醒一个因进行写锁定而被阻塞的 goroutine。


func main() {
    lock := &sync.RWMutex{}
    lock.Lock()

    for i := 0;i < 4;i++{
        go func (i int)  {
            fmt.Printf("第 %d 个协程准备开始... \n",i)
            lock.RLock()
            fmt.Printf("第 %d 个协程获得读锁,sleep 1s后,释放锁\n",i)
            time.Sleep(time.Second)
            lock.RUnlock();
        }(i)
    }

    time.Sleep(time.Second * 2)

    fmt.Println("准备释放写锁,读锁不再阻塞")
    //写锁一释放,读锁就自由了
    lock.Unlock()

    lock.Lock()
    fmt.Println("程序退出...")
    lock.Unlock()
}

在这里插入图片描述

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

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

相关文章

【C++】哈希闭散列

一.哈希的概念 在前面学习了二叉搜索树、AVL树、红黑树之后&#xff0c;我们得知顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系&#xff0c;因此在查找一个元素时&#xff0c;必须经过关键码的多次比较。顺序查找的时间复杂度为 O(N)&#xff0c…

0.CLIP

目录 前言背景缘起/摘要数据集拟解决问题 精读IntroductionModel2.1自然语言监督2.2 创建一个有效的大数据集选择一个有效的预训练方法2.4 选择模型&#xff08;选择Encoder&#xff09;2.5训练小结 实验 复现&#xff08;略&#xff09; 前言 本课程来自深度之眼《多模态》训…

找免费商用的图片素材就上这6个网站。

分享6个免费商用的高清图片素材库&#xff0c;你想要找到这里都能找到&#xff0c;赶紧收藏起来吧~ 菜鸟图库 https://www.sucai999.com/pic.html?vNTYwNDUx 网站主要是为新手设计师提供免费素材的&#xff0c;素材的质量都很高&#xff0c;类别也很多&#xff0c;像平面、UI…

Zhang-Suen骨架提取算法

前言 本专栏针对的目标物体为物体裂缝量化&#xff0c;提取裂缝的骨架有助于裂缝长度的求解&#xff0c;故这一篇也是本专栏的开篇。 细化算法选择与分析 裂缝骨架的提取是十分有必要&#xff0c;如果我们能够得到裂缝的骨架图那么就很容易获得整条裂缝的长度。在当前经典的…

机器学习深度学习——序列模型(NLP启动!)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——卷积神经网络&#xff08;LeNet&#xff09; &#x1f4da;订阅专栏&#xff1a;机器学习&&深度…

地理信息系统空间分析实验教程 第三版 第八章示例与练习 学校选址

学校选址 背景 合理的学校空间位置布局有利于学生的上课与生活。学校的选址问题需要考虑地理 E八位置、学生娱乐场所配套设施、与现有学校的距离等因素&#xff0c;从总体上把握这些国素能够确定出适宜性比较好的学校选址区 目的 通过练习&#xff0c;熟悉 ArcGIS 栅格数据…

无涯教程-Perl - endnetent函数

描述 此功能告诉系统您不再希望使用getnetent从网络列表中读取条目。 语法 以下是此函数的简单语法- endnetent返回值 此函数不返回任何值。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perluse Socket;while ( ($name, $aliases, $addrtype, $net) getnetent() )…

VUE框架:vue2转vue3全面细节总结(3)路由组件传参

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人_python人工智能视觉&#xff08;opencv&#xff09;从入门到实战,前端,微信小程序-CSDN博客 最新的uniapp毕业设计专栏也放在下方了&#xff1a; https://blog.csdn.net/lbcy…

element表格+表单+表单验证结合运用

目录​​​​​​​ 一、结果展示 二、实现代码 一、结果展示 1、图片 2、描述 table中放form表单&#xff0c;放输入框或下拉框或多选框等&#xff1b; 点击添加按钮&#xff0c;首先验证表单&#xff0c;如果存在没填的就验证提醒&#xff0c;都填了就向下添加一行表单表…

Redis中BigKey、HotKey的发现与处理

Redis中BigKey、HotKey的发现与处理 内容详情&#xff1a; 阿里云开发者社区(点击跳转) 参考自&#xff1a; https://developer.aliyun.com/article/788271?utm_contentm_1000291945#slide-1

【数据结构OJ题】删除有序数组中的重复项

原题链接&#xff1a;https://leetcode.cn/problems/remove-duplicates-from-sorted-array/ 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 用双指针算法&#xff0c;定义两个变量src和dst&#xff0c;一开始让src和dst指向num[ ]数组的第一个元素&a…

Cadence学习

Cadence学习 Cadence内容涵盖Cadence主要功能Cadence功能模块Allegro Design Entry CIS 和 OrCAD Capture CIS 的区别Cadence 公司简介Allegro Design Entry CISOrCAD Capture CIS OrCAD中part和database part区别OrCAD中不同页面的连接关系应该怎么处理&#xff08;1&#xff…

Matlab之利用MarkerFaceColor来填充marker

matlab画图在加一些marker的时候, 有实心的圆圈, 比如: plot(x,y,.r,MarkerSize,20)但是如果想要一个很大的marker, 就需要把这个markersize调得很大, 比如MarkerSize20 但是也可以用空心的圆圈然后把中间涂上颜色, 这样调整起来更方便. 比如: plot(x,y,or,MarkerSize,5,Mar…

拆分PDBQT文件并将其转换为PDB格式

拆分PDBQT文件转为PDB格式 1. vina_split拆分PDBQT文件 假设你用AutoDock Vina做了对接&#xff0c;那么所有预测的结合构象都被放入一个多构象 PDBQT 文件中&#xff0c;如果需要拆分后进行可视化分析&#xff0c;那么Vina官方自带了vina_split来进行拆分。下面是vina_split…

TS协议之PES(ES数据包)

TS协议之PAT&#xff08;节目关联表&#xff09;TS协议之PMT&#xff08;节目映射表&#xff09;TS协议之PES&#xff08;ES数据包&#xff09; 该文档已上传&#xff1a;下载地址 1. 概要 1.1 TS数据包&#xff08;PES&#xff09;协议数据组成 TSTS头PES头ES。TS&#xf…

在 Ubuntu 上安装 Docker 桌面

Ubuntu 22.04 (LTS) 安装 Docker 桌面 要成功安装 Docker Desktop&#xff0c;您必须&#xff1a; 满足系统要求拥有 64 位版本的 Ubuntu Jammy Jellyfish 22.04 (LTS) 或 Ubuntu Impish Indri 21.10。对于非 Gnome 桌面环境&#xff0c;必须安装 gnome-terminal&#xff1a;…

springsecurity初稿

springsecurity 课程 课程目标 权限管理简介【了解】权限管理解决方案【掌握】初识Spring Security【了解】Spring Security 认证配置【掌握】Spring Security 鉴权配置【掌握】Spring Security 底层原理【掌握】Spring Security 退出操作【重点】Spring Security整合JWT【重…

参考RabbitMQ实现一个消息队列

文章目录 前言小小消息管家1.项目介绍2. 需求分析2.1 API2.2 消息应答2.3 网络通信协议设计 3. 开发环境4. 项目结构介绍4.1 配置信息 5. 项目演示 前言 消息队列的本质就是阻塞队列&#xff0c;它的最大用途就是用来实现生产者消费者模型&#xff0c;从而实现解耦合以及削峰填…

有什么好用的PNG素材网站吗?看看这6个

高品质PNG素材无疑能提升网站的质量&#xff0c;给用户带来更美好的使用体验&#xff0c;今天本文会与大家分享6个好用的PNG素材网站&#xff0c;一起来看看吧&#xff01; 1、即时设计资源广场 即时设计资源广场集成了多种大厂素材&#xff0c;不只是PNG素材&#xff0c;还有…

试用AI生成代码工具Fauxpilot,详细安装过程

设置服务 预先说明 需要预先安装支持NVIDIA的docker,docker compose > 1.28不能再容器里运行&#xff0c;否则出现以下报错&#xff1a; rootc536ca0dbd64:/test/fauxpilot-main# ./setup.sh Checking for curl ... /usr/bin/curl Checking for zstd ... /opt/conda/bin…