前面已经讲过很多Golang系列知识,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html

接下来要说的是golang的锁的使用场景主要涉及到哪些?读写锁为什么会比普通锁快。

一、什么场景下需要用到锁

当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,有可能是多个线程同时访问公共资源,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢?

1. 多个线程在读相同的数据时
2. 多个线程在写相同的数据时
3. 同一个资源,有读又有写时

二、Go中锁分为两种:

  • 互斥锁 (sync.Mutex)
  • 读写锁 (sync.RWMutex 底层依赖Mutex实现  )

互斥锁是并发程序对公共资源访问限制最常见的方式。在Go中,sync.Mutex 提供了互斥锁的实现。

当一个goroutine获得了Mutex后,其他goroutine只能等待,除非该goroutine释放这个Mutex。

互斥锁结构:

  1. type Mutex struct {
  2. state int32
  3. sema uint32
  4. }

1. 锁定状态值为1,未锁定状态锁未0 。

2. Lock()加锁、Unlock解锁。

读写锁则是对读写操作进行加锁。需要注意的是多个读操作之间不存在互斥关系,这样提高了对共享资源的访问效率。

Go中读写锁由 sync.RWMutex 提供,RWMutex在读锁占用的情况下,会阻止写,但不阻止读。RWMutex在写锁占用情况下,会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该goroutine独占。

读写锁结构:

  1. type RWMutex struct {
  2. w Mutex // held if there are pending writers
  3. writerSem uint32 // semaphore for writers to wait for completing readers
  4. readerSem uint32 // semaphore for readers to wait for completing writers
  5. readerCount int32 // number of pending readers
  6. readerWait int32 // number of departing readers
  7. }

1. RWMutex是单写多读锁,该锁可以加多个读锁或者一个写锁。

2. 读锁占用的情况会阻止写,不会阻止读,多个goroutine可以同时获取读锁。

3. 写锁会阻止其他gorotine不论读或者写进来,整个锁由写锁goroutine占用 与第一条共用示范代码

4. 适用于读多写少的场景

三、如何使用互斥锁

Mutex为互斥锁,Lock() 加锁,Unlock() 解锁,使用Lock() 加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁叶叫做全局锁。

互斥锁只能锁定一次,当在解锁之前再次进行加锁,便会无法加锁。如果在加锁前解锁,便会报错"panic: sync: unlock of unlocked mutex"。

  1. package main
  2. import ("fmt"
  3. "sync"
  4. )
  5.  
  6. var (
  7. count int
  8. lock sync.Mutex
  9. )
  10.  
  11. func main() {
  12. for i := ; i < ; i++ {
  13. go func() {
  14. for i := ; i > ; i-- {
  15. lock.Lock()
  16. count ++
  17. lock.Unlock()
  18. }
  19. fmt.Println(count)
  20. }()
  21. }
  22.  
  23. fmt.Scanf("\n") //等待子线程全部结束
  24. }
  25.  
  26. 运行结果:
  27.  
  28. //最后的线程打印输出

对于上面的程序,a作为一个公共的资源,所以对a的改变、读写等操作都需要加锁。

需要注意的问题:

  1. 不要重复锁定互斥锁
  2. 不要忘记解锁互斥锁,必要时使用 defer 语句
  3. 不要在多个函数之间直接传递互斥锁

四、如何使用读写锁

读写锁的场景主要是在多线程的安全操作下,并且读的情况多于写的情况,也就是说既满足多线程操作的安全性,也要确保性能不能太差,这时候,我们可以考虑使用读写锁。当然你也可以简单暴力直接使用互斥锁(Mutex)。

Lock() 写锁,如果在添加写锁之前已经有其他的读锁和写锁,则lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的 Lock 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定。

Unlock() 写锁解锁,如果没有进行写锁定,则就会引起一个运行时错误。

RLock() 读锁,当有写锁时,无法加载读锁,当只有读锁或者没有锁时,可以加载读锁,读锁可以加载多个,所以适用于"读多写少"的场景。

RUnlock() 读锁解锁,RUnlock 撤销单次RLock 调用,它对于其它同时存在的读取器则没有效果。若 rw 并没有为读取而锁定,调用 RUnlock 就会引发一个运行时错误。

  1. package main
  2. import ("fmt"
  3. "sync"
  4. )
  5.  
  6. var (
  7. count int
  8. rwLock sync.RWMutex
  9. )
  10.  
  11. func main() {
  12. for i := ; i < ; i++ {
  13. go func() {
  14. for i := ; i > ; i-- {
  15. rwLock.Lock()
  16. count ++
  17. rwLock.Unlock()
  18. }
  19. fmt.Println(count)
  20. }()
  21. }
  22.  
  23. fmt.Scanf("\n") //等待子线程全部结束
  24. }
  25.  
  26. 运行结果:

看着挺复杂的,其实简单来说就是:

  1. 读锁不能阻塞读锁

  2. 读锁需要阻塞写锁,直到所有读锁都释放

  3. 写锁需要阻塞读锁,直到所有写锁都释放

  4. 写锁需要阻塞写锁

五、最后

以上,就把golang中各种锁的使用场景及怎么使用互斥锁和读写锁等相关内容介绍完了,希望能对大家有所帮助。

Golang 入门系列(十六)锁的使用场景主要涉及到哪些?读写锁为什么会比普通锁快的更多相关文章

  1. Golang 入门系列(十) mysql数据库的使用

    之前,已经讲过一些Golang的基础的东西,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html, 今天简 ...

  2. Golang 入门系列(六)理解Go中的协程(Goroutine)

    前面讲的都是一些Go 语言的基础知识,感兴趣的朋友可以先看看之前的文章.https://www.cnblogs.com/zhangweizhong/category/1275863.html. 今天就 ...

  3. 无废话ExtJs 入门教程十六[页面布局:Layout]

    无废话ExtJs 入门教程十六[页面布局:Layout] extjs技术交流,欢迎加群(201926085) 首先解释什么是布局: 来自百度词典的官方解释:◎ 布局 bùjú: [distributi ...

  4. S3C2416裸机开发系列十六_sd卡驱动实现

    S3C2416裸机开发系列十六 sd卡驱动实现 象棋小子    1048272975 SD卡(Secure Digital Memory Card)具有体积小.容量大.传输数据快.可插拔.安全性好等长 ...

  5. Bootstrap入门(十六)组件10:well和具有响应式特性的嵌入内容

    Bootstrap入门(十六)组件10:well和具有响应式特性的嵌入内容 well组件可以为内容增添一种切入效果. 具有响应式特性的嵌入内容可以根据被嵌入内容的外部容器的宽度,自动创建一个固定的比例 ...

  6. MyBatis基础入门《十六》缓存

    MyBatis基础入门<十六>缓存 >> 一级缓存 >> 二级缓存 >> MyBatis的全局cache配置 >> 在Mapper XML文 ...

  7. Inno Setup入门(十六)——Inno Setup类参考(2)

    Inno Setup入门(十六)——Inno Setup类参考(2) http://379910987.blog.163.com/blog/static/33523797201112755641236 ...

  8. RabbitMQ入门教程(十六):RabbitMQ与Spring集成

    原文:RabbitMQ入门教程(十六):RabbitMQ与Spring集成 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https: ...

  9. 学习ASP.NET Core Razor 编程系列十六——排序

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

随机推荐

  1. GO基础之流程控制语句

    一.if分支语句 if 布尔表达式 1 { /* 在布尔表达式 1 为 true 时执行 */ } ; a% == { fmt.Println("偶数") } if 布尔表达式 1 ...

  2. IDEA+docker,进行远程漏洞调试(weblogic)

    用于学习java漏洞debug. 以weblogic为例. cve-2017-10271 weblogic:10.3.6 环境搭建 拿docker为例 https://github.com/vulhu ...

  3. 搭建ASP.NET Core WebApi项目

    步骤 从“文件”菜单中选择“新建”>“项目” . 选择“ASP.NET Core Web 应用程序”模板,再单击“下一步” . 将项目命名为 NetCoreWebApi,然后单击“创建” . 选 ...

  4. Xbim.GLTF源码解析(二):IFC和GLTF的对应关系

    原创作者:flowell,转载请标明出处:https://www.cnblogs.com/flowell/p/10839179.html IFC IFC是建筑信息模型(BIM)数据开放的国际标准,在建 ...

  5. SQLAlchemy的基础使用

    一.ORM 与 SQLAlchemy 简介 ORM 全称 Object Relational Mapping, 翻译过来叫对象关系映射.简单的说,ORM 将数据库中的表与面向对象语言中的类建立了一种对 ...

  6. Hibernate 查询方式、JPA查询方式

    hibernate 查询方式: OID 查询 对象导航查询 HQL 方式查询 QBC方式查询 原生SQL方式查询 JPA 查询方式: OID 查询 对象导航查询 JPQL 方式查询 CriteriaB ...

  7. SpringBoot整合Redis在可视化工具乱码问题,以及常用的api

     pom依赖: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spr ...

  8. 高频Linux命令小结(新手向)

    示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 华为云社区地址:[你要的前端打怪升级指南] 近期 ...

  9. 题解【洛谷】CF134A

    题解 CF134A [Average Numbers] 这题就是简单的模拟. 只不过要优化一下常数什么的 思路: 为了不浪费时间总是取平均数,直接用一个 S 储存总和,每次都减去 a_i​ 再除以 n ...

  10. CSP-S:追忆

    Warning:这一篇极其中二,开了那个大会莫名有感而发. 模拟测试17那套题啊... 开的这个大会为什么弄得我退役感如此强烈... 早就想收藏了,还是记下来吧 <入阵曲> 丹青千秋酿, ...