设为首页 收藏本站
查看: 619|回复: 0

[经验分享] golang 互斥锁和读写锁

[复制链接]

尚未签到

发表于 2018-9-20 11:52:51 | 显示全部楼层 |阅读模式
golang中sync包实现了两种锁Mutex(互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能。  type Mutex
  func (m *Mutex) Lock()
  func (m *Mutex) Unlock()
  type RWMutex
  func (rw *RWMutex) Lock()
  func (rw *RWMutex) Unlock()
  func (rw *RWMutex) RLock()
  func (rw *RWMutex) RUnlock()
  func (rw *RWMutex) RLocker() Locker
  1、互斥锁
  func (m *Mutex) Lock():加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁也叫做全局锁。
  func (m *Mutex) Unlock():解锁,如果在使用Unlock()前未加锁,就会引起一个运行错误.已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他goroutine对其解锁。
  互斥锁只能锁定一次,当在解锁之前再次进行加锁,便会死锁状态(导致当前线程阻塞),如果在加锁前解锁,便会报错“panic: sync: unlock of unlocked mutex”
  2、读写锁
  RWMutex是一个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景.
  func (rw *RWMutex) Lock():写锁,如果在添加写锁之前已经有其他的读锁和写锁,则Lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的Lock调用会从获得的锁中排除新的读取锁,即写锁权限高于读锁,有写锁时优先进行写锁定。
  func (rw *RWMutex) Unlock():写锁解锁,如果没有进行写锁定,则就会引起一个运行时错误。
  func (rw *RWMutex) RLock():读锁,当有写锁时,无法加载读锁,当只有读锁或者没有锁时,可以加载读锁,读锁可以加载多个,所以适用于“读多写少”的场景。
  func (rw *RWMutex) RUnlock():读锁解锁,RUnlock 撤销单次RLock调用,它对于其它同时存在的读取器则没有效果。若rw并没有为读取而锁定,调用RUnlock就会引发一个运行时错误。
  读写锁的写锁只能锁定一次,解锁前不能多次锁定,读锁可以多次,但读解锁次数最多只能比读锁次数多一次,一般情况下我们不建议读解锁次数多余读锁次数。
  基本遵循两大原则:
  1、可以随便读,多个goroutine同时读。
  2、写的时候,啥也不能干。不能读也不能写。
  读写锁即是针对于读写操作的互斥锁。它与普通的互斥锁最大的不同就是,它可以分别针对读操作和写操作进行锁定和解锁操作。读写锁遵循的访问控制规则与互斥锁有所不同。在读写锁管辖的范围内,它允许任意个读操作的同时进行。但是在同一时刻,它只允许有一个写操作在进行。
  并且在某一个写操作被进行的过程中,读操作的进行也是不被允许的。也就是说读写锁控制下的多个写操作之间都是互斥的,并且写操作与读操作之间也都是互斥的。但是,多个读操作之间却不存在互斥关系。
  对已被写锁定的读写锁进行写锁定,会造成当前Goroutine的阻塞,直到该读写锁被写解锁。当然如果有多个Goroutine因此而被阻塞,那么当对应的写解锁被进行之时只会使其中一个Goroutine的运行被恢复。类似,对一个已被写锁定的读写锁进行读锁定,也会阻塞相应的Goroutine。
  但不同的是,一旦该读写锁被写解锁,那么所有因欲进行读锁定而被阻塞的Goroutine的运行都会被恢复。另一方面,如果在进行过程中发现当前的读写锁已被读锁定,那么这个写锁定操作将会等待直至所有施加于该读写锁之上的读锁定都被清除。
  同样的,在有多个写锁定操作为此而等待的情况下,相应的读锁定的全部清除只能让其中的某一个写锁定操作获得进行的机会。
  现在来关注写解锁和读解锁。如果对一个未被写锁定的读写锁进行写解锁,那么会引发一个运行时恐慌。类似的,当对一个未被读锁定的读写锁进行读解锁的时候也会引发一个运行时恐慌。写解锁在进行的同时会试图唤醒所有因进行读锁定而被阻塞的Goroutine。
  而读解锁在进行的时候则会试图唤醒一个因进行写锁定而被阻塞的Goroutine。
  无论锁定针对的是写操作还是读操作,我们都应该尽量及时的对相应的锁进行解锁。对于写解锁,我们自不必多说。而读解锁的及时进行往往更容易被我们忽视。虽说读解锁的进行并不会对其他正在进行中的读操作产生任何影响,但它却与相应的写锁定的进行关系紧密。
  注意,对于同一个读写锁来说,施加在它之上的读锁定可以有多个。因此,只有我们对互斥锁进行相同数量的读解锁,才能够让某一个相应的写锁定获得进行的机会。否则,后者会继续使进行它的Goroutine处于阻塞状态。
  由于sync.RWMutex和*sync.RWMutex类型都没有相应的方法让我们获得已进行的读锁定的数量,所以这里是很容易出现问题的。还好我们可以使用defer语句来尽量避免此类问题的发生。请记住,针对同一个读写锁的写锁定和读锁定是互斥的。
  无论是写解锁还是读解锁,操作的不及时都会对使用该读写锁的流程的正常执行产生负面影响。
  除了我们在前面详细讲解的那两对方法之外,*sync.RWMutex类型还拥有另外一个方法——RLocker。这个RLocker方法会返回一个实现了sync.Locker接口的值。sync.Locker接口类型包含了两个方法,即:Lock和Unlock。
  细心的读者可能会发现,*sync.Mutex类型和*sync.RWMutex类型都是该接口类型的实现类型。实际上,我们在调用*sync.RWMutex类型值的RLocker方法之后所得到的结果值就是这个值本身。
  只不过,这个结果值的Lock方法和Unlock方法分别对应了针对该读写锁的读锁定操作和读解锁操作。
  换句话说,我们在对一个读写锁的RLocker方法的结果值的Lock方法或Unlock方法进行调用的时候实际上是在调用该读写锁的RLock方法或RUnlock方法。这样的操作适配在实现上并不困难。我们自己也可以很容易的编写出这些方法的实现。
  通过读写锁的RLocker方法获得这样一个结果值的实际意义在于,我们可以在之后以相同的方式对该读写锁中的“写锁”和“读锁”进行操作。这为相关操作的灵活适配和替换提供了方便。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-598838-1-1.html 上篇帖子: Golang的格式化输出fmt.Printf 下篇帖子: golang 性能测试pprof
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表