Go并发同步Mutex典型易错使用场景
作者:BeCautious 发布时间:2024-05-02 16:23:54
Mutex的4种易错使用场景
1.Lock/Unlock 不成对出现
Lock/Unlock 没有成对出现,就可能会出现死锁或者是因为Unlock一个未加锁的Mutex而导致 panic。
忘记Unlock的情形
代码中有太多的 if-else 分支,可能在某个分支中漏写了 Unlock;
在重构的时候把 Unlock 给删除了;
Unlock 误写成了 Lock。
忘记Lock的情形一般是误删除了或者注释掉了Lock。
eg:
func main() {
var mu sync.Mutex
defer mu.Unlock()
fmt.Println("oh, missing Lock!")
}
error result:
2.Copy 已使用的 Mutex
实际上sync包下的同步原语在使用后都是不可复制的,原因在于Mutex是有状态的,其state的值时刻在变化,如果复制一个已经加锁的Metux对象给一个新的变量,可能这个变量刚初始化就显示被加锁了,这显然是不合理的。
eg:以下代码在调用 foo 函数的时候,调用者会复制 Mutex 变量 c 作为 foo 函数的参数,不幸的是,复制之前已经使用了这个锁,这就导致,复制的 Counter 是一个带状态 Counter,从而会导致死锁。
type Counter struct {
sync.Mutex
Count int
}
func main() {
var c Counter
c.Lock()
defer c.Unlock()
c.Count++
foo(c) // 复制锁
}
// 这里Counter的参数是通过复制的方式传入的
func foo(c Counter) {
c.Lock()
defer c.Unlock()
fmt.Println("in foo")
}
error result:还好有Go的协程死锁检查机制,程序运行后会快速失败而不是一直hang住。
Go Vet指令
我们当然不想程序运行了才发现死锁,我们可以通过go vet指令来在运行前检查我们的代码是否存在lock copy问题:
检查原理
检查是通过copylock分析器静态分析实现的。这个分析器会分析函数调用、range 遍历、复制、声明、函数返回值等位置,有没有锁的值 copy 的情景,以此来判断有没有问题。
通过源码我们可以看到实现了Lock或者Unlock接口的struct都支持copylock检查。
var lockerType *types.Interface
// Construct a sync.Locker interface type.
func init() {
nullary := types.NewSignature(nil, nil, nil, false) // func()
methods := []*types.Func{
types.NewFunc(token.NoPos, nil, "Lock", nullary),
types.NewFunc(token.NoPos, nil, "Unlock", nullary),
}
lockerType = types.NewInterface(methods, nil).Complete()
}
3.重入
Mutex不像Java中的ReentrantLock拥有可重入的功能,主要是因为其实现中没有标记位记录哪个goroutine 拥有这把锁,所以Mutex是一个不可重入锁,而一旦误用Mutex的重入就会报错。
eg:
func foo(l sync.Locker) {
fmt.Println("in foo")
l.Lock()
bar(l)
l.Unlock()
}
func bar(l sync.Locker) {
l.Lock()
fmt.Println("in bar")
l.Unlock()
}
func main() {
l := &sync.Mutex{}
foo(l)
}
error result:我们可以看到当在bar方法中尝试再次获取锁时,获取不到,触发了死锁。
4.死锁
两个或两个以上的进程(或线程,goroutine)
执行过程中,因争夺共享资源而处于一种互相等待的状态,如果没有外部干涉,它们都将无法推进下去,此时,我们称系统处于死锁状态或系统产生了死锁。
死锁产生的4个必要条件
如果想避免死锁,我们只要思考如何打破以下任意条件就可以。
1.互斥: 至少一个资源是被排他性独享的,其他线程必须处于等待状态,直到资源被释放。
2.持有和等待:goroutine 持有一个资源,并且还在请求其它 goroutine 持有的资源,也就是咱们常说的“吃着碗里,看着锅里”的意思。
3. 不可剥夺:资源只能由持有它的 goroutine 来释放。
4.环路等待:一般来说,存在一组等待进程,P={P1,P2,…,PN},P1 等待 P2 持有的
资源,P2 等待 P3 持有的资源,依此类推,最后是 PN 等待 P1 持有的资源,这就形成
了一个环路等待的死结。
eg:在这里我们以办理居住证业务,举一个简单的环路等待导致死锁的例子:
//办理居住证
func main() {
// 网签中心证明
var psCertificate sync.Mutex
// 社区证明
var propertyCertificate sync.Mutex
var wg sync.WaitGroup
wg.Add(2) // 需要网签中心和社区都处理
// 网签中心处理goroutine
go func() {
defer wg.Done() // 网签中心处理完成
psCertificate.Lock()
defer psCertificate.Unlock()
// 检查材料
time.Sleep(5 * time.Second)
// 请求社区的证明
propertyCertificate.Lock()
propertyCertificate.Unlock()
}()
// 社区处理goroutine
go func() {
defer wg.Done() // 社区处理完成
propertyCertificate.Lock()
defer propertyCertificate.Unlock()
// 检查材料
time.Sleep(5 * time.Second)
// 请求网签中心的证明
psCertificate.Lock()
psCertificate.Unlock()
}()
wg.Wait()
fmt.Println("成功完成")
}
error result:
解决策略
1.可以引入一个第三方的锁,大家都依赖这个锁进行业务处理,比如现在政府推行的一站式政务服务中心。
2.解决持有等待问题,比如社区不需要看到网签中心的证明才给开居住证明。
来源:https://juejin.cn/post/7101531692698959886


猜你喜欢
- Python是一门面向对象的语言。面向对象都有三大特性:封装、继承、多态。下面分别来说说这三大特性:1、封装隐藏对象的属性和实现细节,仅对外
- 前言这篇博文发布后,有朋友问有没有SQL server版本的,现在有了==》传送门一、场景再现在一个erp进销存系统或0A等其他系统中,如果
- 在仓库目录下新建一个名为.gitignore的文件(因为是点开头,没有文件名,没办法直接在windows目录下直接创建,必须通过右键Git
- 我们经常会遇到多重查询问题,而长长的SQL语句往往让人丈二和尚摸不着头脑。特别是客户端部分填入查询条件时,如用普通方法将更是难上加难。以下巧
- 首先让我们来看看有关 Perl 面向对象编程的三个基本定义:1. 一个“对象”是指一个“有办法知道它是属于哪个类”的简单引用。(
- 共有三种推导式列表(list)推导式字典(dict)推导式集合(set)推导式列表推导式基本语法:[out_express for out_
- 目的:是学习python 多线程的工作原理,及通过抓取400张图片这种IO密集型应用来查看多线程效率对比import requestsimp
- MySQL死锁问题是很多程序员在项目开发中常遇到的问题,现就MySQL死锁及解决方法详解如下:1、MySQL常用存储引擎的锁机制MyISAM
- MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。M
- 下面介绍两种查看django 执行的sql语句的方法。方法一:queryset = Apple.objects.all()print que
- 这篇文章主要介绍了如何基于python实现归一化处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友
- 前言之前说了怎么写机器码到内存,然后调用。现在说说怎么优化。用Python发送微信消息给好友第二次优化再看一遍c语言的代码void Send
- 前言在文档对象模型 (DOM) 中,每个节点都是一个对象。DOM 节点有三个重要的属性 :1. nodeName : 节点的名称2. nod
- 一,编程环境PyCharm2016,Anaconda3 Python3.6需要安装schedule模块,该模块网址:https://pypi
- python安装教程和Pycharm安装详细教程,分享给大家。首先我们来安装python1、首先进入网站下载:点击打开链接(或自己输入网址h
- Numpy是什么很简单,Numpy是Python的一个科学计算的库,提供了矩阵运算的功能,其一般与Scipy、matplotlib一起使用。
- 环境OpenCV3.4.16(C++)opencv-contrib-python 4.5.4.60(Python)验证Opencv函数fil
- WordPress可以改造成twitter一样的微博网站,但是有一个坏处就是你要么用来做博客要么用来做微博,功能难兼得。相信大家在访问一些知
- 本文实例讲述了Python基于dom操作xml数据的方法。分享给大家供大家参考,具体如下:1、xml的内容为del.xml,如下<?x
- 代码如下:use tempdb if object_id('tempdb..#table') is not null dro