我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Golang中的RWMutex怎么使用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Golang中的RWMutex怎么使用

本篇内容主要讲解“Golang中的RWMutex怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang中的RWMutex怎么使用”吧!

    RWMutex 的整体模型

    正如 RWMutex 的命名那样,它是区分了读锁和写锁的锁,所以我们可以从读和写两个方面来看 RWMutex 的模型。

    下文中的 reader 指的是进行读操作的 goroutine,writer 指的是进行写操作的 goroutine。

    读操作模型

    我们可以用下图来表示 RWMutex 的读操作模型:

    Golang中的RWMutex怎么使用

    上图使用了 w.Lock,是因为 RWMutex 的实现中,写锁是使用 Mutex 来实现的。

    说明:

    • 读操作的时候可以同时有多个 goroutine 持有 RLock,然后进入临界区。(也就是可以并行读),上图的 G1G2G3 就是同时持有 RLock 的几个 goroutine。

    • 在读操作的时候,如果有 goroutine 持有 RLock,那么其他 goroutine (不管是读还是写)就只能等待,直到所有持有 RLock 的 goroutine 释放锁。

    • 也就是上图的 G4 需要等待 G1G2G3 释放锁之后才能进入临界区。

    • 最后,因为 G5G6 这两个协程获取锁的时机比 G4 晚,所以它们会在 G4 释放锁之后才能进入临界区。

    写操作模型

    我们可以用下图来表示 RWMutex 的写操作模型:

    Golang中的RWMutex怎么使用

    说明:

    写操作的时候只能有一个 goroutine 持有 Lock,然后进入临界区,释放写锁之前,所有其他的 goroutine 都只能等待。

    上图的 G1~G5 表示的是按时间顺序先后获取锁的几个 goroutine。

    上面几个 goroutine 获取锁的过程是:

    • G1 获取写锁,进入临界区。然后 G2G3G4G5 都在等待。

    • G1 释放写锁之后,G2G3 可以同时获取读锁,进入临界区。然后 G3G4G5 都在等待。

    • G2G3 可以同时获取读锁,进入临界区。然后 G4G5 都在等待。

    • G2G3 释放读锁之后,G4 获取写锁,进入临界区。然后 G5 在等待。

    • 最后,G4 释放写锁,G5 获取读锁,进入临界区。

    基本用法

    RWMutex 中包含了以下的方法:

    • Lock:获取写锁,如果有其他 goroutine 持有读锁或写锁,那么就会阻塞等待。

    • Unlock:释放写锁。

    • RLock:获取读锁,如果有其他 goroutine 持有写锁,那么就会阻塞等待。

    • RUnlock:释放读锁。

    其他不常用的方法:

    • RLocker:返回一个读锁,该锁包含了 RLockRUnlock 方法,可以用来获取读锁和释放读锁。

    • TryLock: 尝试获取写锁,如果获取成功,返回 true,否则返回 false。不会阻塞等待。

    • TryRLock: 尝试获取读锁,如果获取成功,返回 true,否则返回 false。不会阻塞等待。

    一个简单的例子

    我们可以通过下面的例子来看一下 RWMutex 的基本用法:

    package muteximport (   "sync"   "testing")var config map[string]stringvar mu sync.RWMutexfunc TestRWMutex(t *testing.T) {   config = make(map[string]string)   // 启动 10 个 goroutine 来写   var wg1 sync.WaitGroup   wg1.Add(10)   for i := 0; i < 10; i++ {      go func() {         set("foo", "bar")         wg1.Done()      }()   }   // 启动 100 个 goroutine 来读   var wg2 sync.WaitGroup   wg2.Add(100)   for i := 0; i < 100; i++ {      go func() {         get("foo")         wg2.Done()      }()   }   wg1.Wait()   wg2.Wait()}// 获取配置func get(key string) string {   // 获取读锁,可以多个 goroutine 并发读取   mu.RLock()   defer mu.RUnlock()   if v, ok := config[key]; ok {      return v   }   return ""}// 设置配置func set(key, val string) {   // 获取写锁   mu.Lock()   defer mu.Unlock()   config[key] = val}

    上面的例子中,我们启动了 10 个 goroutine 来写配置,启动了 100 个 goroutine 来读配置。 这跟我们现实开发中的场景是一样的,很多时候其实是读多写少的。 如果我们在读的时候也使用互斥锁,那么就会导致读的性能非常差,因为读操作一般都不会有副作用的,但是如果使用互斥锁,那么就只能一个一个的读了。

    而如果我们使用 RWMutex,那么就可以同时有多个 goroutine 来读取配置,这样就可以大大提高读的性能。 因为我们进行读操作的时候,可以多个 goroutine 并发读取,这样就可以大大提高读的性能。

    RWMutex 使用的注意事项

    在《深入理解 go Mutex》中,我们已经讲过了 Mutex 的使用注意事项, 其实 RWMutex 的使用注意事项也是差不多的:

    • 不要忘记释放锁,不管是读锁还是写锁。

    • Lock 之后,没有释放锁之前,不能再次使用 Lock

    • Unlock 之前,必须已经调用了 Lock,否则会 panic

    • 在第一次使用 RWMutex 之后,不能复制,因为这样一来 RWMutex 的状态也会被复制。这个可以使用 go vet 来检查。

    源码剖析

    RWMutex 的一些实现原理跟 Mutex 是一样的,比如阻塞的时候使用信号量等,在 Mutex 那一篇中已经有讲解了,这里不再赘述。 这里就 RWMutex 的实现原理进行一些简单的剖析。

    RWMutex 结构体

    RWMutex 的结构体定义如下:

    type RWMutex struct {   w           Mutex        // 互斥锁,用于保护读写锁的状态   writerSem   uint32       // writer 信号量   readerSem   uint32       // reader 信号量   readerCount atomic.Int32 // 所有 reader 数量   readerWait  atomic.Int32 // writer 等待完成的 reader 数量}

    各字段含义:

    • w:互斥锁,用于保护读写锁的状态。RWMutex 的写锁是互斥锁,所以直接使用 Mutex 就可以了。

    • writerSem:writer 信号量,用于实现写锁的阻塞等待。

    • readerSem:reader 信号量,用于实现读锁的阻塞等待。

    • readerCount:所有 reader 数量(包括已经获取读锁的和正在等待获取读锁的 reader)。

    • readerWait:writer 等待完成的 reader 数量(也就是获取写锁的时刻,已经获取到读锁的 reader 数量)。

    因为要区分读锁和写锁,所以在 RWMutex 中,我们需要两个信号量,一个用于实现写锁的阻塞等待,一个用于实现读锁的阻塞等待。 我们需要特别注意的是 readerCountreaderWait 这两个字段,我们可能会比较好奇,为什么有了 readerCount 这个字段, 还需要 readerWait 这个字段呢?

    这是因为,我们在尝试获取写锁的时候,可能会有多个 reader 正在使用读锁,这时候我们需要知道有多少个 reader 正在使用读锁, 等待这些 reader 释放读锁之后,就获取写锁了,而 readerWait 这个字段就是用来记录这个数量的。 在 Lock 中获取写锁的时候,如果观测到 readerWait 不为 0 则会阻塞等待,直到 readerWait 为 0 之后才会真正获取写锁,然后才可以进行写操作。

    读锁源码剖析

    获取读锁的方法如下:

    // 获取读锁func (rw *RWMutex) RLock() {   if rw.readerCount.Add(1) < 0 {      // 有 writer 在使用锁,阻塞等待 writer 完成      runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)   }}

    读锁的实现很简单,先将 readerCount 加 1,如果加 1 之后的值小于 0,说明有 writer 正在使用锁,那么就需要阻塞等待 writer 完成。

    释放读锁的方法如下:

    // 释放读锁func (rw *RWMutex) RUnlock() {   // readerCount 减 1,如果 readerCount 小于 0 说明有 writer 在等待   if r := rw.readerCount.Add(-1); r < 0 {      // 有 writer 在等待,唤醒 writer      rw.rUnlockSlow(r)   }}// 唤醒 writerfunc (rw *RWMutex) rUnlockSlow(r int32) {   // 未 Lock 就 Unlock,panic   if r+1 == 0 || r+1 == -rwmutexMaxReaders {      fatal("sync: RUnlock of unlocked RWMutex")   }   // readerWait 减 1,返回值是新的 readerWait 值   if rw.readerWait.Add(-1) == 0 {      // 最后一个 reader 唤醒 writer      runtime_Semrelease(&rw.writerSem, false, 1)   }}

    读锁的实现总结:

    • 获取读锁的时候,会将 readerCount 加 1

    • 如果正在获取读锁的时候,发现 readerCount 小于 0,说明有 writer 正在使用锁,那么就需要阻塞等待 writer 完成。

    • 释放读锁的时候,会将 readerCount 减 1

    • 如果 readerCount 减 1 之后小于 0,说明有 writer 正在等待,那么就需要唤醒 writer。

    • 唤醒 writer 的时候,会将 readerWait 减 1,如果 readerWait 减 1 之后为 0,说明 writer 获取锁的时候存在的 reader 都已经释放了读锁,可以获取写锁了。

    &middot;rwmutexMaxReaders算是一个特殊的标识,在获取写锁的时候会将readerCount的值减去rwmutexMaxReaders, 所以在其他地方可以根据 readerCount` 是否小于 0 来判断是否有 writer 正在使用锁。

    写锁源码剖析

    获取写锁的方法如下:

    // 获取写锁func (rw *RWMutex) Lock() {   // 首先,解决与其他写入者的竞争。   rw.w.Lock()   // 向读者宣布有一个待处理的写入。   // r 就是当前还没有完成的读操作,等这部分读操作完成之后才可以获取写锁。   r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders   // 等待活跃的 reader   if r != 0 && rw.readerWait.Add(r) != 0 {      // 阻塞,等待最后一个 reader 唤醒      runtime_SemacquireRWMutex(&rw.writerSem, false, 0)   }}

    释放写锁的方法如下:

    // 释放写锁func (rw *RWMutex) Unlock() {   // 向 readers 宣布没有活动的 writer。   r := rw.readerCount.Add(rwmutexMaxReaders)   if r >= rwmutexMaxReaders { // r >= 0 并且 < rwmutexMaxReaders 才是正常的(r 是持有写锁期间尝试获取读锁的 reader 数量)      fatal("sync: Unlock of unlocked RWMutex")   }   // 如果有 reader 在等待写锁释放,那么唤醒这些 reader。   for i := 0; i < int(r); i++ {      runtime_Semrelease(&rw.readerSem, false, 0)   }   // 允许其他的 writer 继续进行。   rw.w.Unlock()}

    写锁的实现总结:

    • 获取写锁的时候,会将 readerCount 减去 rwmutexMaxReaders,这样就可以区分读锁和写锁了。

    • 如果 readerCount 减去 rwmutexMaxReaders 之后不为 0,说明有 reader 正在使用读锁,那么就需要阻塞等待这些 reader 释放读锁。

    • 释放写锁的时候,会将 readerCount 加上 rwmutexMaxReaders

    • 如果 readerCount 加上 rwmutexMaxReaders 之后大于 0,说明有 reader 正在等待写锁释放,那么就需要唤醒这些 reader。

    TryRLock 和 TryLock

    TryRLockTryLock 的实现都很简单,都是尝试获取读锁或者写锁,如果获取不到就返回 false,获取到了就返回 true,这两个方法不会阻塞等待。

    // TryRLock 尝试锁定 rw 以进行读取,并报告是否成功。func (rw *RWMutex) TryRLock() bool {   for {      c := rw.readerCount.Load()      // 有 goroutine 持有写锁      if c < 0 {         return false      }      // 尝试获取读锁      if rw.readerCount.CompareAndSwap(c, c+1) {         return true      }   }}// TryLock 尝试锁定 rw 以进行写入,并报告是否成功。func (rw *RWMutex) TryLock() bool {   // 写锁被占用   if !rw.w.TryLock() {      return false   }   // 读锁被占用   if !rw.readerCount.CompareAndSwap(0, -rwmutexMaxReaders) {      // 释放写锁      rw.w.Unlock()      return false   }   // 成功获取到锁   return true}

    到此,相信大家对“Golang中的RWMutex怎么使用”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    Golang中的RWMutex怎么使用

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    Golang中的RWMutex怎么使用

    本篇内容主要讲解“Golang中的RWMutex怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang中的RWMutex怎么使用”吧!RWMutex 的整体模型正如 RWMutex
    2023-07-05

    Golang并发之RWMutex怎么使用

    这篇文章主要介绍“Golang并发之RWMutex怎么使用”,在日常操作中,相信很多人在Golang并发之RWMutex怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Golang并发之RWMutex怎
    2023-07-05

    Golang并发之RWMutex的用法详解

    在 Go 语言中,RWMutex 是一种读写互斥锁的实现,它提供了一种简单有效的方式来管理对共享资源的并发访问。本文就来和大家详细聊聊RWMutex的用法吧
    2023-05-14

    GoLang中的互斥锁Mutex和读写锁RWMutex使用教程

    RWMutex是一个读/写互斥锁,在某一时刻只能由任意数量的reader持有或者一个writer持有。也就是说,要么放行任意数量的reader,多个reader可以并行读;要么放行一个writer,多个writer需要串行写
    2023-01-09

    一文带你深入理解Golang中的RWMutex

    这篇文章主要为大家详细介绍了Golang中RWMutex的相关知识,知其然,更要知其所以然。文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2023-05-14

    golang中的.()怎么使用

    这篇文章主要介绍“golang中的.()怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“golang中的.()怎么使用”文章能帮助大家解决问题。什么是.()用法?在golang中,.()被称为
    2023-07-05

    Golang中的Mutex怎么使用

    本篇内容介绍了“Golang中的Mutex怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Mutex基本概念Mutex 是 Go 语言
    2023-07-05

    Golang中的WaitGroups怎么使用

    本篇内容介绍了“Golang中的WaitGroups怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是WaitGroupsWait
    2023-07-05

    Golang中的interface怎么使用

    这篇文章主要讲解了“Golang中的interface怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang中的interface怎么使用”吧!万能类型interface在Jav
    2023-06-27

    Golang中的sync.Cond怎么使用

    本文小编为大家详细介绍“Golang中的sync.Cond怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Golang中的sync.Cond怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1. 基本
    2023-07-05

    Golang中的sync.Map怎么使用

    今天小编给大家分享一下Golang中的sync.Map怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。map 在并发下
    2023-07-05

    golang中怎么使用gopath

    小编给大家分享一下golang中怎么使用gopath,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!golang gopath的新用法从使用golang开发项目以来
    2023-06-14

    golang中的注释怎么使用

    本篇内容主要讲解“golang中的注释怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“golang中的注释怎么使用”吧!一、单行注释单行注释是在代码行的末尾添加注释的方式,以“//”开头。
    2023-07-05

    golang中channel怎么使用

    今天小编给大家分享一下golang中channel怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。什么是 channe
    2023-06-29

    golang中defer怎么使用

    这篇文章主要介绍“golang中defer怎么使用”,在日常操作中,相信很多人在golang中defer怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”golang中defer怎么使用”的疑惑有所帮助!
    2023-07-02

    Golang中的init ()函数怎么使用

    本文小编为大家详细介绍“Golang中的init ()函数怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Golang中的init ()函数怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Go in
    2023-07-04

    Golang语言中的map怎么使用

    这篇文章主要介绍“Golang语言中的map怎么使用”,在日常操作中,相信很多人在Golang语言中的map怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Golang语言中的map怎么使用”的疑惑有所
    2023-07-05

    怎么使用golang开发中的godep

    本篇内容主要讲解“怎么使用golang开发中的godep”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么使用golang开发中的godep”吧!介绍godep是解决包依赖的管理工具,目前最主流
    2023-06-25

    Ring怎么在Golang中使用

    今天就跟大家聊聊有关Ring怎么在Golang中使用,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。golang的优点golang是一种编译语言,可以将代码编译为机器代码,编译后的二进
    2023-06-14

    bytes.Buffer怎么在golang中使用

    bytes.Buffer怎么在golang中使用?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。go适合做什么go是golang的简称,而golang可以做服务器
    2023-06-14

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录