前面已经讲过很多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。

互斥锁结构:

type Mutex struct {
state int32
sema uint32
}

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

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

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

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

读写锁结构:

type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing readers
}

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

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

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

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

三、如何使用互斥锁

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

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

package main
import ("fmt"
"sync"
) var (
count int
lock sync.Mutex
) func main() {
for i := ; i < ; i++ {
go func() {
for i := ; i > ; i-- {
lock.Lock()
count ++
lock.Unlock()
}
fmt.Println(count)
}()
} fmt.Scanf("\n") //等待子线程全部结束
} 运行结果: //最后的线程打印输出

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

需要注意的问题:

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

四、如何使用读写锁

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

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

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

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

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

package main
import ("fmt"
"sync"
) var (
count int
rwLock sync.RWMutex
) func main() {
for i := ; i < ; i++ {
go func() {
for i := ; i > ; i-- {
rwLock.Lock()
count ++
rwLock.Unlock()
}
fmt.Println(count)
}()
} fmt.Scanf("\n") //等待子线程全部结束
} 运行结果:

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

  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. 玩转OneNET物联网平台之HTTP服务③ —— OneNet智能灯 HTTP版本

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  2. redis入门(一)

    目录 redis入门(一) 前言 特性 速度快 简单稳定 丰富的功能 历史 历史版本 安装与启动 安装 数据类型与内部编码 数据结构 内部编码 常用API与使用场景 常用命令 字符串 列表 哈希 集合 ...

  3. 让SpringBoot的jackson支持JavaBean嵌套的protobuf

    问题背景 REST 项目使用protobuf 来加速项目开发,定义了很多model,vo,最终返回的仍然是JSON. 项目中一般使用 一个Response类, public class Respons ...

  4. Linux杂谈:解决配置静态ip后eth0网卡启动不了的问题

    今天在看imooc上的<Linux网络管理>的课程中,在做一些实验时修改了下网络配置,发现了一些问题,就是保存网络配置后eth0网卡打不开,可能也会有很多人出现这类问题,我就在这里分享下自 ...

  5. Java后端开发工作 - 写接口

    我在公司的工作内容是,对于一个BS应用,负责服务器端开发工作,Java语言.与前端开发人员合作,最终提供给前端RESTFUL接口,保证页面正常响应. 经验之谈 一个接口可以理解为一个业务逻辑,一个业务 ...

  6. CSPS模拟 50

    收获很多,良心出题人 T1 施工 研究半天,最后30分暴力走人 考后看了题解,稍神仙这题弃对了...... 要拿30+,必须发现要填的话一定是填一个坑使它底部变平,最终底部高度小于等于两边 为什么是坑 ...

  7. csps模拟测试 77爆零反思

    题不算太难,可是我还是没考出应有水平. $1h8min$切掉前两道题,然后$T3$想到正解并且码出来了并且过了大样例并且爆零. 没什么好说的,我太自信了,没打对拍? 想到了正解,还不如随便打个暴力分高 ...

  8. js取两位小数点

    var money = one.money; var tmoney = money.substr(0,money.indexOf(".")+3);

  9. NOI导刊总结

    NOI导刊总结 前两天去郑州,参加了什么NOI导刊的培训,然后就发现大佬是真的多,还十分意外的发现了一个事,清华北大是不是发笔记本和耳机,为啥三个老师的都一模一样... 这几天主要以讲.NOIP知识点 ...

  10. 九:写了一下红帽免费的centos6的安装步骤

    linux centos 6安装方法 前提需要: 1, centos6的镜像文件 2,VMware 提前安装 注:获取镜像 阿里开源系统,此处可下载其他的 1.Ubuntu 2.Susa 3.Cent ...