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
mutex sync.Mutex
) func main() {
for i := 0; i < 2; i++ {
go func() {
for i := 1000000; i >0; i-- {
mutex.Lock()
count++
mutex.Unlock()
}
fmt.Println(count)
}()
} fmt.Scanf("请输入:【exit】 结束")
}
运行结果:
1952533
2000000 //最后的线程打印输出
对于上面的程序,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
rwMutex sync.RWMutex
) func main() {
for i := 0; i < 2; i++ {
go func() {
for i := 1000000; i >0; i-- {
rwMutex.Lock()
count++
rwMutex.Unlock()
}
fmt.Println(count)
}()
} fmt.Scanf("请输入:【exit】 结束")
}
运行结果:
1968637
2000000
看着挺复杂的,其实简单来说就是:
读锁不能阻塞读锁
读锁需要阻塞写锁,直到所有读锁都释放
写锁需要阻塞读锁,直到所有写锁都释放
写锁需要阻塞写锁
golang中锁的更多相关文章
- golang中锁mutex的实现
golang中的锁是通过CAS原子操作实现的,Mutex结构如下: type Mutex struct { state int32 sema uint ...
- golang 互斥锁和读写锁
golang 互斥锁和读写锁 golang中sync包实现了两种锁Mutex(互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. ty ...
- golang中并发sync和channel
golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"go",但是如何处理go并发机制中不同goroutine之间的同步与通信,golang 中提供了sync包和channel ...
- golang中sync.RWMutex和sync.Mutex区别
golang中sync包实现了两种锁Mutex (互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. type Mutex f ...
- 【协作式原创】查漏补缺之Golang中mutex源码实现(预备知识)
预备知识 CAS机制 1. 是什么 参考附录3 CAS 是项乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是 ...
- 【协作式原创】查漏补缺之Golang中mutex源码实现
概览最简单版的mutex(go1.3版本) 预备知识 主要结构体 type Mutex struct { state int32 // 指代mutex锁当前的状态 sema uint32 // 信号量 ...
- golang中mutex使用
package main import ( "fmt" "sync" "time" ) /* 高并发是golang语言最大的亮点 一个线程可 ...
- golang 中 sync.Mutex 的实现
mutex 的实现思想 mutex 主要有两个 method: Lock() 和 Unlock() Lock() 可以通过一个 CAS 操作来实现 func (m *Mutex) Lock() { f ...
- 延宕执行,妙用无穷,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中defer关键字延迟调用机制使用EP17
先行定义,延后执行.不得不佩服Go lang设计者天才的设计,事实上,defer关键字就相当于Python中的try{ ...}except{ ...}finally{...}结构设计中的finall ...
随机推荐
- IDEA微服务项目SpringBoot一键(批量)顺序启动
找到 搜索 RunDashboard <option name="configurationTypes"> <set> <option value=& ...
- ubuntu web服务器配置
1.安装Apachesudo apt-get install apache2 查看状态: service apache2 status/start/stop/restartWeb目录: /var/ww ...
- 【LeetCode】718. Maximum Length of Repeated Subarray 解题报告(Python)
[LeetCode]718. Maximum Length of Repeated Subarray 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxu ...
- hdu-1237简单计算器(栈的运用)
http://acm.hdu.edu.cn/showproblem.php?pid=1237 简单的栈的运用. 首先将数字和运算符分离,分别保存在两个数组中,然后按原来的式子的顺序,首先将第一个数和第 ...
- Pikachu漏洞练习-SQL-inject(二)
- Windows服务注册(需要指定config文件的情况下)
最近,遇到一个问题:需要将telegraf在Win平台下注册为windows服务(避免误操作关闭CMD窗口): 尝试了网上的几种注册Windows服务的方法,发现无法将telegraf这种需要在CMD ...
- CS起源-havana地图红方打法分析
作者:海底淤泥 havana是美国第一人称射击游戏<反恐精英>中的地图之一,编号为cs_havana,这张地图发生在古巴哈瓦那的某座城市中,恐怖分子们挟持了几名美裔的重要政治人物,以此为筹 ...
- Chapter 11 Why Model ?
目录 11.1 Data cannot speak for themselves 11.2 Parametric estimators of the conditional mean 11.3 Non ...
- element ui el-tree回显问题及提交问题(当后台返回的el-tree相关数组的时候,子菜单未全部选中,但是只要父级菜单的id在数组中,那么他的子菜单为全部选中状态)
1:问题原因:我们可能使用 this.$refs.tree.setCheckedKeys(this.defalutArr);或者使用:default-expanded-keys="treeD ...
- Java Web程序设计笔记 • 【第2章 JSP基础】
全部章节 >>>> 本章目录 2.1 JSP 简介 2.1.1 JSP 概述 2.1.2 开发第一个 JSP 页面 2.1.3 JSP 处理流程 2.1.4 实践练习 2. ...