当前位置:网站首页>Golang 互斥锁
Golang 互斥锁
2022-08-03 12:47:00 【云满笔记】
1. Golang 互斥锁
1.1. 基础知识
对写操作的锁定和解锁, 简称"写锁定"和"写解锁":
func (*RWMutex) Lock()
func (*RWMutex) Unlock()
对读操作的锁定和解锁, 简称为"读锁定"与"读解锁":
func (*RWMutex) RLock()
func (*RWMutex) RUnlock()
看个不使用锁的示例:
func printer(str string) {
for _, data := range str {
fmt.Printf("%c", data)
}
fmt.Println()
}
func person1() {
printer("hello")
}
func person2() {
printer("world")
}
func main() {
go person1()
person2()
time.Sleep(time.Second)
} //输出结果//worhello//ld
加上互斥锁的示例:
var mut sync.Mutex
func printer(str string) {
mut.Lock()
defer mut.Unlock()
for _, data := range str {
fmt.Printf("%c", data)
}
fmt.Println()
}
func person1() {
printer("hello")
}
func person2() {
printer("world")
}
func main() {
go person1()
person2()
time.Sleep(time.Second)
} //输出结果//world//hello
1.2. 注意事项
1.2.1. 互斥锁
- 不要重复锁定互斥锁: 对一个已经被锁定的互斥锁进行锁定, 是会立即阻塞当前的 goroutine, 这个 goroutine 所执行的流程, 会一直停滞在调用该互斥锁的 Lock 方法的那行代码上。(注意: 这种由 Go 语言运行时系统自行抛出的 panic 都属于致命错误, 都是无法被恢复的, 调用 recover 函数对它们起不到任何作用。也就是说, 一旦产生死锁, 程序必然崩溃。)
- 不要忘记解锁互斥锁, 必要时使用 defer 语句: 因为在一个 goroutine 执行的流程中, 可能会出现诸如"锁定、解锁、再锁定、再解锁"的操作, 所以如果我们忘记了中间的解锁操作, 那就一定会造成重复锁定。
var mutex sync.Mutex
func write() {
defer mutex.Unlock() // 通过 defer 解锁
mutex.Lock() // 获取临界资源, 执行具体逻辑。..
}
- 不要对尚未锁定或者已解锁的互斥锁解锁: 这个程序会直接 panic。
var mutex sync.Mutex // 定义互斥锁变量 mutex
func main() {
mutex.Lock()
mutex.Unlock()
mutex.Unlock() // fatal error: sync: unlock of unlocked mutexreturn
}
- 不要在多个函数之间直接传递互斥锁: 互斥锁是一结构体类型, 即值类型, 把它传给一个函数、将它从函数中返回、把它赋给其他变量、让它进入某个通道都会导致它的副本的产生。因此, 原值和它的副本、以及多个副本之间都是完全独立的, 它们都是不同的互斥锁。
1.2.2. 读写锁
- 在写锁已被锁定的情况下再试图锁定写锁, 会阻塞当前的 goroutine;
- 在写锁已被锁定的情况下试图锁定读锁, 也会阻塞当前的 goroutine;
- 在读锁已被锁定的情况下试图锁定写锁, 同样会阻塞当前的 goroutine;
- 在读锁已被锁定的情况下再试图锁定读锁, 并不会阻塞当前的 goroutine;
- 解锁"读写锁中未被锁定的写锁", 会立即引发 panic, 对于读锁也是如此。
上面写的有点啰嗦, 我用大白话总结一下: 我读数据时, 你可以去读, 因为我两的数据是一样的; 我读数据时, 你不能写, 你写了, 数据就变了, 我还读个鬼啊; 我写数据时, 你不能读, 也不能写, 我就是这么强势。下面看一个实例:
var count int
var mutex sync.RWMutex
func write(n int) {
rand.Seed(time.Now().UnixNano())
fmt.Printf("写 goroutine %d 正在写数据。..\n", n)
mutex.Lock()
num := rand.Intn(500)
count = num
fmt.Printf("写 goroutine %d 写数据结束, 写入新值 %d\n", n, num)
mutex.Unlock()
}
func read(n int) {
mutex.RLock()
fmt.Printf("读 goroutine %d 正在读取数据。..\n", n)
num := count
fmt.Printf("读 goroutine %d 读取数据结束, 读到 %d\n", n, num)
mutex.RUnlock()
}
func main() {
for i := 0; i < 10; i++ {
go read(i + 1)
}
for i := 0; i < 10; i++ {
go write(i + 1)
}
time.Sleep(time.Second * 5)
}
输出结果:
读 goroutine 1 正在读取数据。..
读 goroutine 1 读取数据结束, 读到 0
读 goroutine 7 正在读取数据。..
读 goroutine 7 读取数据结束, 读到 0
读 goroutine 3 正在读取数据。..
读 goroutine 3 读取数据结束, 读到 0
读 goroutine 10 正在读取数据。..
读 goroutine 10 读取数据结束, 读到 0
读 goroutine 8 正在读取数据。..
读 goroutine 8 读取数据结束, 读到 0
读 goroutine 6 正在读取数据。..
读 goroutine 5 正在读取数据。..
读 goroutine 5 读取数据结束, 读到 0
写 goroutine 2 正在写数据。..
读 goroutine 4 正在读取数据。..
读 goroutine 4 读取数据结束, 读到 0
写 goroutine 4 正在写数据。..
写 goroutine 3 正在写数据。..
读 goroutine 2 正在读取数据。..
读 goroutine 2 读取数据结束, 读到 0
写 goroutine 9 正在写数据。..
读 goroutine 6 读取数据结束, 读到 0
写 goroutine 7 正在写数据。..
读 goroutine 9 正在读取数据。..
读 goroutine 9 读取数据结束, 读到 0
写 goroutine 6 正在写数据。..
写 goroutine 1 正在写数据。..
写 goroutine 8 正在写数据。..
写 goroutine 10 正在写数据。..
写 goroutine 5 正在写数据。..
写 goroutine 2 写数据结束, 写入新值 365
写 goroutine 4 写数据结束, 写入新值 47
写 goroutine 3 写数据结束, 写入新值 468
写 goroutine 9 写数据结束, 写入新值 155
写 goroutine 7 写数据结束, 写入新值 112
写 goroutine 6 写数据结束, 写入新值 490
写 goroutine 1 写数据结束, 写入新值 262
写 goroutine 8 写数据结束, 写入新值 325
写 goroutine 10 写数据结束, 写入新值 103
写 goroutine 5 写数据结束, 写入新值 353
可以看出前面 10 个协程可以并行读取数据, 后面 10 个协程, 就全部阻塞在了"… 正在写数据。…"过程, 等读完了, 然后 10 个协程就开始依次写。
1.3. 总结
本章的内容不多, 主要需要注意互斥锁和读写锁的几条注意事项, 读写锁其实就是更细粒度的锁划分, 为了能让程序更好并发, 上面已经讲述的非常清楚, 这里就不再啰嗦。唯一再强调的一点, 无论是互斥锁还是读写锁, 我们都不要试图去解锁未锁定的锁, 因为这样会引发不可恢复的 panic。
边栏推荐
- Redis连接池工具类
- An动画优化之遮罩层动画
- Oracle is installed (system disk) and transferred from the system disk to the data disk
- [Verilog] HDLBits Problem Solution - Circuits/Sequential Logic/Latches and Flip-Flops
- leetcode16最接近的三数之和 (排序+ 双指针)
- 类型转换、常用运算符
- 【必读要点】Pod控制器Deployment更新、回退详解
- leetcode 11. The container that holds the most water
- BOM系列之sessionStorage
- 汉源高科G8032标准ERPS环网交换机千兆4光10电工业以太网交换机环网+WEB管理+SNMP划VLAN
猜你喜欢
HCIP 第十六天笔记(SVI、生成树协议)
How can I get a city's year-round weather data for free?Precipitation, temperature, humidity, solar radiation, etc.
Station B responded that "HR said that core users are all Loser": the interviewer was persuaded to quit at the end of last year and will learn lessons to strengthen management
【实战技能】单片机bootloader的CANFD,I2C,SPI和串口方式更新APP视频教程(2022-08-01)
PyTorch框架训练线性回归模型(CPU与GPU环境)
PyTorch构建神经网络预测气温(数据集对比,CPU与GPU对比)
云计算服务主要安全风险及应对措施初探
Yahoo!Answers - data set
业界新标杆!阿里开源自研高并发编程核心笔记(2022最新版)
图像融合SDDGAN文章学习
随机推荐
软件测试面试(四)
leetcode 11. The container that holds the most water
汉源高科G8032标准ERPS环网交换机千兆4光10电工业以太网交换机环网+WEB管理+SNMP划VLAN
Graphic animation and button animation of an animation basic component
【Verilog】HDLBits题解——验证:阅读模拟
云计算服务主要安全风险及应对措施初探
为冲销量下探中低端市场,蔚来新品牌产品定价低至10万?
Tinymce plugins [Tinymce扩展插件集合]
从器件物理级提升到电路级
HCIP第十五天笔记(企业网的三层架构、VLAN以及VLAN 的配置)
Sogou news - dataset
An动画优化之补间形状与传统补间的优化
An工具介绍之骨骼工具
Comics: how do you prove that sleep does not release the lock, and wait to release lock?
From the physical level of the device to the circuit level
When Nodejs installation depends on cpnm, the install shows Error: Cannot find module 'fs/promises'
基于php旅游网站管理系统获取(php毕业设计)
基于php校园医院门诊管理系统获取(php毕业设计)
shell编程之条件语句
通过点击CheckBox实现背景变换小案例