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

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

[复制链接]

尚未签到

发表于 2018-9-20 09:06:25 | 显示全部楼层 |阅读模式
  一、互斥锁
  互斥锁是传统的并发程序对共享资源进行访问控制的主要手段。它由标准库代码包sync中的Mutex结构体类型代表。sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开方法——Lock和Unlock。顾名思义,前者被用于锁定当前的互斥量,而后者则被用来对当前的互斥量进行解锁。
  类型sync.Mutex的零值表示了未被锁定的互斥量。也就是说,它是一个开箱即用的工具。我们只需对它进行简单声明就可以正常使用了,就像这样:
复制代码 代码如下:  var mutex sync.Mutex
  mutex.Lock()
  在我们使用其他编程语言(比如C或Java)的锁类工具的时候,可能会犯的一个低级错误就是忘记及时解开已被锁住的锁,从而导致诸如流程执行异常、线程执行停滞甚至程序死锁等等一系列问题的发生。然而,在Go语言中,这个低级错误的发生几率极低。其主要原因是有defer语句的存在。
  我们一般会在锁定互斥锁之后紧接着就用defer语句来保证该互斥锁的及时解锁。请看下面这个函数:
复制代码 代码如下:  var mutex sync.Mutex
  func write() {
  mutex.Lock()
  defer mutex.Unlock()
  // 省略若干条语句
  }
  函数write中的这条defer语句保证了在该函数被执行结束之前互斥锁mutex一定会被解锁。这省去了我们在所有return语句之前以及异常发生之时重复的附加解锁操作的工作。在函数的内部执行流程相对复杂的情况下,这个工作量是不容忽视的,并且极易出现遗漏和导致错误。所以,这里的defer语句总是必要的。在Go语言中,这是很重要的一个惯用法。我们应该养成这种良好的习惯。
  对于同一个互斥锁的锁定操作和解锁操作总是应该成对的出现。如果我们锁定了一个已被锁定的互斥锁,那么进行重复锁定操作的Goroutine将会被阻塞,直到该互斥锁回到解锁状态。请看下面的示例:
复制代码 代码如下:  func repeatedlyLock() {
  var mutex sync.Mutex
  fmt.Println("Lock the lock. (G0)")
  mutex.Lock()
  fmt.Println("The lock is locked. (G0)")
  for i := 1; i  int(df.dataLen) {
  bytes = d[0:df.dataLen]
  } else {
  bytes = d
  }
  df.fmutex.Lock()
  df.fmutex.Unlock()
  _, err = df.f.Write(bytes)
  return
  }
  这里需要注意的是,当参数d的值的长度大于数据块的最大长度的时候,我们会先进行截短处理再将数据写入文件。如果没有这个截短处理,我们在后面计算的已读数据块的序列号和已写数据块的序列号就会不正确。
  有了编写前面两个方法的经验,我们可以很容易的编写出*myDataFile类型的Rsn方法和Wsn方法:
复制代码 代码如下:  func (df *myDataFile) Rsn() int64 {
  df.rmutex.Lock()
  defer df.rmutex.Unlock()
  return df.roffset / int64(df.dataLen)
  }
  func (df *myDataFile) Wsn() int64 {
  df.wmutex.Lock()
  defer df.wmutex.Unlock()
  return df.woffset / int64(df.dataLen)
  }
  这两个方法的实现分别涉及到了对互斥锁rmutex和wmutex的锁定操作。同时,我们也通过使用defer语句保证了对它们的及时解锁。在这里,我们对已读数据块的序列号rsn和已写数据块的序列号wsn的计算方法与前面示例中的方法是相同的。它们都是用相关的偏移量除以数据块长度后得到的商来作为相应的序列号(或者说计数)的值。
  至于*myDataFile类型的DataLen方法的实现,我们无需呈现。它只是简单地将dataLen字段的值作为其结果值返回而已。
  编写上面这个完整示例的主要目的是展示互斥锁和读写锁在实际场景中的应用。由于还没有讲到Go语言提供的其他同步工具,所以我们在相关方法中所有需要同步的地方都是用锁来实现的。然而,其中的一些问题用锁来解决是不足够或不合适的。我们会在本节的后续部分中逐步的对它们进行改进。
  从这两种锁的源码中可以看出,它们是同源的。读写锁的内部是用互斥锁来实现写锁定操作之间的互斥的。我们可以把读写锁看做是互斥锁的一种扩展。除此之外,这两种锁实现在内部都用到了操作系统提供的同步工具——信号灯。互斥锁内部使用一个二值信号灯(只有两个可能的值的信号灯)来实现锁定操作之间的互斥,而读写锁内部则使用一个二值信号灯和一个多值信号灯(可以有多个可能的值的信号灯)来实现写锁定操作与读锁定操作之间的互斥。当然,为了进行精确的协调,它们还使用到了其他一些字段和变量


运维网声明 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-598635-1-1.html 上篇帖子: golang/TLS 采坑 下篇帖子: Golang的Json encode/decode以及[]byte和string的转换
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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