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 ...
随机推荐
- tomcat启动报错There is insufficient memory for the Java Runtime Environment to continue
tomcat启动报错后显示以下错误 ## There is insufficient memory for the Java Runtime Environment to continue.# Nat ...
- Nacos配置中心+ASP.NET Core
Nacos配置中心 nacos 是一个构建云原生应用的动态服务发现.配置管理和服务管理平台.. 源码已上传至 github 配置管理 asp.net core中所有的配置项,如appsetting.j ...
- 【LeetCode】1419. 数青蛙 Minimum Number of Frogs Croaking (Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 日期 题目地址:https://leetcode ...
- 【LeetCode】243. Shortest Word Distance 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 日期 题目地址:https://leetcode ...
- Codeforces 339B:Xenia and Ringroad(水题)
time limit per test : 2 seconds memory limit per test : 256 megabytes input : standard input output ...
- 【机器学*】k*邻算法-03
心得体会: 需要思考如何将现实对象转化为特征向量,设置特征向量时记住鸭子定律1 鸭子定律1 如果走路像鸭子.说话像鸭子.长得像鸭子.啄食也像鸭子,那它肯定就是一只鸭子 事物的外在特征就是事物本质的表现 ...
- Log4自定义Appender介绍
最初想要在执行一段业务逻辑的时候调用一个外部接口记录审计信息,一直找不到一个比较优雅的方式,经过讨论觉得log4j自定义的appender或许可以实现此功能.后来就了解了一下log4j的这部分. Ap ...
- ECMA-262规范定义的七种错误类型
第一种:Error 所有错误的基本类型,实际上不会被抛出. 第二种:EvalError 执行eval错误时抛出. 第三种:ReferenceError 对象不存在是抛出. 第四种: ...
- Spring Boot 整合 Fisco Bcos(区块链)
简介 FISCO BCOS是由国内企业主导研发.对外开源.安全可控的企业级金融联盟链底层平台,由金链盟开源工作组协作打造,并于2017年正式对外开源. 目前,成熟的区块链的平台不少,之所以选择FISC ...
- 教你如何6秒钟往MySQL插入100万条数据!然后删库跑路!
教你如何6秒钟往MySQL插入100万条数据!然后删库跑路! 由于我用的mysql 8版本,所以增加了Timezone,然后就可以了 前提是要自己建好库和表. 数据库test, 表user, 三个字段 ...