文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

Go并发之RWMutex源码分析

2023-07-05 12:34

关注

这篇文章主要介绍“Go并发之RWMutex源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go并发之RWMutex源码分析”文章能帮助大家解决问题。

RWMutex是一个支持并行读串行写的读写锁。RWMutex具有写操作优先的特点,写操作发生时,仅允许正在执行的读操作执行,后续的读操作都会被阻塞。

使用场景

RWMutex常用于大量并发读,少量并发写的场景;比如微服务配置更新、交易路由缓存等场景。相对于Mutex互斥锁,RWMutex读写锁具有更好的读性能。

下面以 “多个协程并行读取str变量,一个协程每100毫秒定时更新str变量” 场景为例,进行RWMutex读写锁和Mutex互斥锁的性能对比。

// 基于RWMutex的实现var rwLock sync.RWMutexvar str1 = "hello"func readWithRWLock() string {    rwLock.RLock()    defer rwLock.RUnlock()    return str1}func writeWithRWLock() {    rwLock.Lock()    str1 = time.Now().Format("20060102150405")    rwLock.Unlock()}// 多个协程并行读取string变量,同时每100ms对string变量进行1次更新func BenchmarkRWMutex(b *testing.B) {    ticker := time.NewTicker(100 * time.Millisecond)    go func() {        for range ticker.C {            writeWithRWLock()        }    }()    b.ResetTimer()    b.RunParallel(func(pb *testing.PB) {        for pb.Next() {            readWithRWLock()        }    })}// 基于Mutex实现var lock sync.Mutexvar str2 = "hello"func readWithMutex() string {    lock.Lock()    defer lock.Unlock()    return str2}func writeWithMutex() {    lock.Lock()    str2 = time.Now().Format("20060102150405")    lock.Unlock()}// 多个协程并行读取string变量,同时每100ms对string变量进行1次更新func BenchmarkMutex(b *testing.B) {    ticker := time.NewTicker(100 * time.Millisecond)    go func() {        for range ticker.C {            writeWithMutex()        }    }()    b.ResetTimer()    b.RunParallel(func(pb *testing.PB) {        for pb.Next() {            readWithMutex()        }    })}

RWMutex读写锁和Mutex互斥锁的性能对比,结果如下:

# go test 结果
go test -bench . -benchtime=10s
BenchmarkRWMutex-8      227611413               49.5 ns/op
BenchmarkMutex-8        135363408               87.8 ns/op
PASS
ok      demo    37.800s

源码解析

RWMutex是一个写操作优先的读写锁,如下图所示:

Go并发之RWMutex源码分析

RWMutex结构体

RWMutex由如下变量组成:

const rwmutexMaxReaders = 1 << 30type RWMutex struct {    w           Mutex  // Mutex互斥锁,用于实现写操作之间的互斥    writerSem   uint32 // 写操作信号量,用于读操作唤醒写操作    readerSem   uint32 // 读操作信号量,用于写操作唤醒读操作    readerCount int32  // 读操作的数量,不存在写操作时从0开始计数,存在写操作时从-rwmutexMaxReaders开始计数    readerWait  int32  // 写操作等待读操作的数量}

Lock()方法

Lock方法用于写操作获取锁,其操作如下:

func (rw *RWMutex) Lock() {    // 写操作之间通过w互斥锁实现互斥    rw.w.Lock()    // 1.将readerCount更新为负值,表示当前有写操作;当readerCount为负数时,新的读操作会被挂起    // 2.r表示当前正在执行的读操作数量    r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders    // r != 0表示当前存在正在执行的读操作;写操作需要等待所有读操作执行完,才能被执行;    if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {        // 将写操作挂起        runtime_SemacquireMutex(&rw.writerSem, false, 0)    }}

Unlock()方法

Unlock方法用于写操作释放锁,其操作如下:

readerCount更新为正数,表示当前不存在活跃的写操作;

如果更新后的readerCount大于0,表示当前写操作阻塞了readerCount个读操作,需要将所有被阻塞的读操作都唤醒;

w互斥锁释放,允许其他写操作执行;

func (rw *RWMutex) Unlock() {    // 将readerCount更新为正数,从0开始计数    r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)    if r >= rwmutexMaxReaders {        throw("sync: Unlock of unlocked RWMutex")    }    // 唤醒所有等待写操作的读操作     for i := 0; i < int(r); i++ {        runtime_Semrelease(&rw.readerSem, false, 0)    }    // 释放w互斥锁,允许其他写操作进入    rw.w.Unlock()}

RLock()方法

RLock方法用于读操作获取锁,其操作如下:

func (rw *RWMutex) RLock() {    // 原子更新readerCount+1    // 1. readerCount+1为负数时,表示当前存在写操作;读操作需要等待写操作执行完,才能被执行    // 2. readerCount+1不为负数时,表示当前不存在写操作,读操作可以执行    if atomic.AddInt32(&rw.readerCount, 1) < 0 {        // 将读操作挂起        runtime_SemacquireMutex(&rw.readerSem, false, 0)    }}

RUnlock()方法

RUnlock方法用于读操作释放锁,其操作如下:

原子更新readerCount-1

如果当前读操作阻塞了写操作atomic.AddInt32(&rw.readerCount, -1)<0,原子更新readerWait-1

readerWait为0时,表示阻塞写操作的所有读操作都执行完了,唤醒写操作;

func (rw *RWMutex) RUnlock() {    // 原子更新readerCount-1    // 当readerCount-1为负时,表示当前读操作阻塞了写操作,需要进行readerWait的更新    if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {        rw.rUnlockSlow(r)    }}func (rw *RWMutex) rUnlockSlow(r int32) {    if r+1 == 0 || r+1 == -rwmutexMaxReaders {        throw("sync: RUnlock of unlocked RWMutex")    }    // 原子操作readerWait-1    // 当readerWait-1为0时,表示导致写操作阻塞的所有读操作都执行完,将写操作唤醒    if atomic.AddInt32(&rw.readerWait, -1) == 0 {        // 唤醒读操作        runtime_Semrelease(&rw.writerSem, false, 1)    }}

关于“Go并发之RWMutex源码分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

阅读原文内容投诉

免责声明:

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

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

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯