转自公众号“灰子学技术”,原文链接:https://mp.weixin.qq.com/s/XBCcgQsk7WRE9rC2xn6ZiA
写在前面的话:程序中的并发,是导致临界区竞争的根本原因,而解决这个问题的最常用办法就是锁机制,而mutex是Go语言之中最基本的一种锁机制。
这篇文章,笔者主要来介绍下Go语言的这个锁机制mutex,在开始之前,我们需要先介绍下几个概念。
1. 基本概念
临界区(Critical Section): 当程序并发地运行时,多个Go协程不应该同时访问那些修改共享资源的代码,这些修改共享资源的代码称为临界区。
竞态条件:它旨在描述一个系统或者进程的输出依赖于不受控制的事件出现顺序或者出现时机。举例来说,如果计算机中的两个进程同时试图修改一个共享内存的内容,在没有并发控制的情况下,最后的结果依赖于两个进程的执行顺序与时机。而且如果发生了并发访问冲突,则最后的结果是不正确的。
锁:是在执行并发时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足。一般的锁是建议锁(advisory lock),每个线程在访问对应资源前都需获取锁的信息,再根据信息决定是否可以访问。若访问对应信息,锁的状态会改变为锁定,因此其他线程此时不会访问该资源,当资源结束后,会恢复锁的状态,允许其他线程的访问。
2.mutex介绍
在了解了上面的基本概念之后,我们来看下Go语言中的mutex。
mutex 用于提供一种加锁机制,可确保在某时刻只有一个协程在临界区运行,以防止出现竞态条件。
mutexs是Go的sync包中的一个数据结构,定义了两个方法:Lock 和 Unlock。所有在 Lock 和 Unlock 之间的代码,都只能由一个 Go 协程执行,于是就可以避免竞态条件。
mutex的特点如下所示:
1. Mutex 为互斥锁,Lock() 加锁,Unlock() 解锁
2. 在一个 goroutine 获得 Mutex 后,其他 goroutine 只能等到这个 goroutine 释放该 Mutex
3. 使用 Lock() 加锁后,不能再继续对其加锁,直到利用 Unlock() 解锁后才能再加锁
4. 在 Lock() 之前使用 Unlock() 会导致 panic 异常
5. 已经锁定的 Mutex 并不与特定的 goroutine 相关联,这样可以利用一个 goroutine 对其加锁,再利用其他 goroutine 对其解锁
6. 在同一个 goroutine 中的 Mutex 解锁之前再次进行加锁,会导致死锁
7. 适用于读写不确定,并且只有一个读或者写的场景
1) 不用mutex的例子:
output:
结果分析:我们执行了5次程序,发现输出的结果并不一致。原因就是 这100个协程,产生了竞态关系,导致计算的结果,并不是100, 而是变成了不确定的值。
2)使用mutex的例子:(我们通过mutex的锁机制来解决这个问题)
Output:
结果分析:在加了mutex锁之后,我们执行了很多次结果都是100,那是因为mutex的锁机制保证了x=x+1的操作在一个协程执行的时候,不会被其他进程打断,所以每一次运行的结果都是100。
灰子学技术:
有疑问加站长微信联系(非本文作者)