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

淮南市重点工程建设管理局网站优化网站推广网站

淮南市重点工程建设管理局网站,优化网站推广网站,wap网站开发多少钱,织梦门户网站源码下载在简略的说之前,首先要对RW锁的结构有一个大致的了解 type RWMutex struct {w Mutex // 写锁互斥锁,只锁写锁,和读锁无关writerSem uint32 // sema锁--用于“写协程”排队等待readerSem uint32 // sema锁--用于“读协程”排队…

在简略的说之前,首先要对RW锁的结构有一个大致的了解

type RWMutex struct {w           Mutex  // 写锁互斥锁,只锁写锁,和读锁无关writerSem   uint32 // sema锁--用于“写协程”排队等待readerSem   uint32 // sema锁--用于“读协程”排队等待readerCount int32  // 读锁的计数器readerWait  int32  // 等待读锁释放的数量
}

这里要额外说一句,writerSem和readerSem底层都是semaRoot,这个结构体有兴趣可以了解下,他的用法有点类似于一个简版的channel,很多地方把他的初始值设置为0,使得所有想获取该sema锁的协程都排队等待,也就是说初始值为0的sema锁,他本身起到的作用是成为一个协程等待队列,就像没有缓冲区的channel一样。

好现在进入正题。本文是为了在面试中能快速口述RW锁,并非为了完整解答RW锁的机制。

前提:

readerCount这个参数非常重要

  • 为负数时:说明此锁已经被写协程占据,所有渴望加读锁的协程被阻塞在readerSem
  • 为正数时:正数的数值为当前持有该锁的所有读协程的数量总和,所有渴望加写锁的协程被阻塞在writerSem

读写锁互斥性

  • 读锁是并发的,可以多个协程持有一把读锁。
  • 写锁是唯一的,互斥的,同一时刻只能有一个写协程拥有写锁
  • 读锁和写锁是互斥的,写锁生效时,是不能有读锁被获取到,同样,必须所有的读锁都被释放,或者压根没有读协程获取读锁,写锁方可被获取。

一个很重要的参数:const rwmutexMaxReaders = 1 << 30 ,rwmutexMaxReaders 非常大,意思是最多能有rwmutexMaxReaders(1 << 30  十进制为  4294967296)个协程同时持有读锁。


写锁上锁场景:

首先分析写锁,因为读锁的很多操作是根据写锁来的,如果一上来就说读锁,很多东西没法串起来

       

func (rw *RWMutex) Lock() {// race.Enabled是官方的一些测试,性能检测的东西,无需关心,这个只在编译阶段才能启用if race.Enabled {_ = rw.w.staterace.Disable()}// First, resolve competition with other writers.rw.w.Lock()// Announce to readers there is a pending writer.r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders// Wait for active readers.if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {runtime_SemacquireMutex(&rw.writerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))race.Acquire(unsafe.Pointer(&rw.writerSem))}
}
  1. 获取写锁--没有读锁等待

    1. rw.w.Lock进行加锁,阻塞后续的其他写协程的锁请求。
    2. atomic.AddInt32进行原子操作,减去rwmutexMaxReaders,减成功才说明没有并发问题,可以继续下面的操作。然后再加上rwmutexMaxReaders,得到真正的readerCount的数值。
    3. 此时还需要再次进行一个原子操作,把当前readerCount的值搬运到readerWait里面,意思是当前要获取写锁的协程需要等待的读锁的数量。
    4. 此时readerCount只有两种情况,一种是0,一种是正数,因为只有写锁上的时候才为负数,而上面的操作已经还原了加写锁之前的值,而w.Lock保证了不会有2个及以上的写协程去同时操作
    5. readerCount 如果是 0,加锁成功。
    6. 如果不为0则说明有读锁等待,详见场景2
  2. 获取写锁--有读锁等待

    1. 接上面的判断,如果readrCount不为0,说明前面有读锁正在运行,写锁需要等待所有读锁释放才能获取写锁,当前协程执行 runtime_SemacquireMutex 进入 waiterSem 的休眠队列等待被唤醒
  3. 获取写锁--前面已经有写锁了

    1. 后面的写协程也调用 rw.w.Lock() 进行加锁,因为前面有写锁已经获取了w,所以后续的写协程会因为获取不到w,而进入到w的sema队列里面,w是一个mutex的锁,mutex锁里是一个sema锁,sema锁因为没有设置初始值,所以退化为一个队列,而获取不到w锁的就会直接被阻塞在w的sema队列里,从而无法进行接下来的操作

写锁释放锁场景:

func (rw *RWMutex) Unlock() {if race.Enabled {_ = rw.w.staterace.Release(unsafe.Pointer(&rw.readerSem))race.Disable()}// Announce to readers there is no active writer.r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)if r >= rwmutexMaxReaders {race.Enable()throw("sync: Unlock of unlocked RWMutex")}// Unblock blocked readers, if any.for i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false, 0)}// Allow other writers to proceed.rw.w.Unlock()if race.Enabled {race.Enable()}
}
  1. 释放写锁--后面【没有】读锁等待

    1. 执行atomic.AddInt32进行原子操作,把已经为负值的readerCount还原为正数,此时已经算释放了写锁
    2. (此步骤不重要,就是个判错)如果还原后的readerCount比rwmutexMaxReaders还大,这就是说明出错了,直接throw弹出错误,throw这个方法是内部方法,对go来说就是panic了
    3. 此场景因为没有读锁等待,此时的readerCount为0,不会进入for循环,直接rw.w.Unlock释放w锁,允许其他写协程加锁,此时其他的写协程会被从w里的sema队列里唤醒
  2. 释放写锁--后面【有】读锁等待

    1. 接场景1,原子操作readerCount释放写锁后,如果r是大于0,说明有读锁等待,for循环readerSem里面所有的等待的读协程,因为读锁是共享锁,所以所有的读协程都会获取锁并被唤醒
    2. rw.w.Unlock释放w锁,允许其他写协程加锁,其他的写协程会被从w里的sema队列里唤醒
  3. 释放写锁--后面有【写锁】等待

    1. 上接场景2,当rw.w.Unlock释放w锁,其他的写协程会被从w里的sema队列里唤醒
    2. 写锁释放的时候,是先唤醒所有等待的读锁,再解除rw.w锁,所以,并不会造成读锁的饥饿
    3. 后面读锁再次对rw.w进行上锁,重复上面所述写锁获取锁的场景


读锁上锁场景:

func (rw *RWMutex) RLock() {// race.Enabled都是测试用的代码,在阅读源码的时候可以跳过if race.Enabled {_ = rw.w.staterace.Disable()}if atomic.AddInt32(&rw.readerCount, 1) < 0 {// A writer is pending, wait for it.runtime_SemacquireMutex(&rw.readerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))}
}
  1. 获取读锁--此时没有写锁.

         最简单的场景,协程对rw.readerCount进行原子操作加一,如果得到的结果为正数,说明获取读锁成功。
  2. 获取读锁--前方已经有写锁抢占了该锁

    1.  当协程对rw.readerCount进行原子加1操作的时候,发现加完,readerCount还是负数,说明在这个时间点以前,已经有协程获取了写锁
    2.  runtime_SemacquireMutex 方法将当前协程加入readerSem队列,等待写锁释放后被批量唤醒(写锁释放会一次性放出所有的堆积的读协程)
  3. 获取读锁--前方有写锁抢已经被抢占,后方有写锁等待

          写锁在获取的时候,对RWMutex.w进行加锁,是独占锁,如果前方一个写锁已经得到了锁正在处理业务,那么后方的写锁进来就会发现加不上锁,直接在rw.w.lock阶段就阻塞了,后面的逻辑是无法继续运行的,所以进入不了writerSem,它只会进入到w这个mutex锁的sema队列里,读锁则进入休眠队列readerSem

读锁释放锁场景:

func (rw *RWMutex) RUnlock() {if race.Enabled {_ = rw.w.staterace.ReleaseMerge(unsafe.Pointer(&rw.writerSem))race.Disable()}if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {// Outlined slow-path to allow the fast-path to be inlinedrw.rUnlockSlow(r)}if race.Enabled {race.Enable()}
}
  1. 释放读锁--后方没有写锁等待

    1. atomic.AddInt32 进行原子操作,让readerCount 减1,操作后,如果readerCount 大于0,说明后方是没有写锁等待的,释放锁后整个流程就结束了
  2. 释放读锁--后方有写锁等待

    1. 原子操作eaderCount 减1后,发现eaderCount是小于0的,此时说明已经有等待写锁的协程在尝试获取写锁。执行 rw.rUnlockSlow(r) 。

                

func (rw *RWMutex) rUnlockSlow(r int32) {if r+1 == 0 || r+1 == -rwmutexMaxReaders {race.Enable()throw("sync: RUnlock of unlocked RWMutex")}// A writer is pending.if atomic.AddInt32(&rw.readerWait, -1) == 0 {// The last reader unblocks the writer.runtime_Semrelease(&rw.writerSem, false, 1)}
}

             这里是有个前提的,上面提到(详见上面的获取写锁的场景1),如果写协程进来想加写锁,需要把它需要等待的读锁数量从readerCount里赋值给readerWait。当它等待的读锁释放后,就需要用rUnlockSlow方法对readerWait进行减1,如果readWait == 0 ,说明这是最后一个需要等待的读锁也释放了,释放后就通知该写锁可以被唤醒了,锁给你了。

http://www.fp688.cn/news/163855.html

相关文章:

  • 网站哪个公司做的好手游推广代理平台有哪些
  • 重庆网站建设的价格低公司网站设计与制作
  • 做一个像qq空间的网站全球搜索网站排名
  • 阿里云做网站电话网络推广用什么软件好
  • 有了源代码如何做网站外包网络推广公司
  • 桃城网站建设代理怎么进行seo
  • 官方关停13家网站百度安装app
  • 美妆网站开发规划书营销公司网站
  • 做一个网站大概要多少钱东莞网络营销公司
  • 制作精美网站建设独立搜索引擎在线观看
  • 公众号开发专业旺道seo软件
  • 无人在线观看高清视频8怎么优化关键词
  • 网站编辑seo销售外包
  • 武汉高端网站定制设计师今日要闻
  • 武汉高端网站建设公司营销型网站建设总结
  • 北京海淀网站建设公司免费广告投放网站
  • 做网站能成功吗广州网站制作公司
  • 个人操作做网站排名百度精准搜索
  • 台州网站建站公司seo是什么seo怎么做
  • 网站制作前景北京做网页的公司
  • 东莞企业网站制重庆seo技术博客
  • 现场直播cctv5直播吧北京网站优化企业
  • 佛山建站互联网营销师报名官网
  • 初中做语文题的网站最好最全的搜索引擎
  • 蛋糕教做网站最新长尾关键词挖掘
  • 找人做app网站吗申请域名的方法和流程
  • wordpress html5支持关键词智能优化排名
  • 域名防红跳转网址生成seo推广有哪些公司
  • 内容转载的网站怎么做软文发布平台
  • 官方查企业的网站精准营销平台