Go语言互斥锁

Go语言互斥锁教程

锁的作用就是某个 协程 (线程)在访问某个资源时先锁住,防止其它协程的访问,等访问完毕解锁后其他协程再来加锁进行访问。

Go 语言 中的 sync 包提供了两种锁类型,分别为:sync.Mutex 和 sync.RWMutex,即互斥锁和 读写锁

死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。

死锁是锁的一种错误使用状态,编程过程中,不应出现死锁的情况。

互斥锁

每个资源都对应于一个可称为 “互斥锁” 的标记,这个标记用来保证在任意时刻,只能有一个协程(线程)访问该资源。其它的协程只能等待。

互斥锁是传统并发编程对共享资源进行访问控制的主要手段,在 Golang 中,它由标准库 sync 中的 Mutex 结构体类型 表示。sync.Mutex 类型 只有两个公开的指针方法,Lock 和 Unlock。Lock 锁定当前的共享资源,Unlock 进行解锁。

在使用互斥锁时,一定要注意:对资源操作完成后,一定要解锁,否则会出现流程执行异常,死锁等问题。通常借助 defer。锁定后,立即使用 defer 语句保证互斥锁及时解锁。

互斥锁与读写锁

Mutex 是最简单的一种锁类型,同时也比较暴力,当一个 goroutine 获得了 Mutex 后,其他 goroutine 就只能乖乖等到这个 goroutine 释放该 Mutex。

RWMutex 相对友好些,是经典的单写多读模型。在读锁占用的情况下,会阻止写,但不阻止读,也就是多个 goroutine 可同时获取读锁(调用 RLock() 方法;而写锁(调用 Lock() 方法)会阻止任何其他 goroutine(无论读和写)进来,整个锁相当于由该 goroutine 独占。

互斥锁使用

语法

// 定义互斥锁变量 mutex var mutex sync.Mutex // 对需要访问的资源加锁 mutex.Lock( ) // 资源访问结束解锁 mutex.Unlock( )

说明

互斥锁的使用,首先是定义一个 sync.Mutex 类型的 变量,接着对需要加锁访问的资源使用 mutex.Lock( ) 进行加锁即可,最后,资源访问结束,使用 mutex.Unlock( ) 进行解锁即可。

案例

互斥锁

使用 sync.Mutex 互斥锁加锁操作

package main import ( "fmt" ) var( // 计数器 count int ) // 返回当前计数器的值 func Count()int{ return count } // 对计数器的值加一 func IncCount(){ count++ } func main() { fmt.Println("嗨客网(www.haicoder.net)") IncCount() count := Count() fmt.Println("Count =", count) }

程序运行后,控制台输出如下:

12_golang mutex.png

首先,我们定义了一个 int 类型的全部变量 count ,同时,我们创建了一个 Count 函数 和一个 IncCount 函数,Count 函数返回全局变量 count 的值,IncCount 函数将全局变量 count 的值加一。

最后,在 main 函数,我们首先使用 IncCount 函数,将 count 的值加一,接着使用 Count 函数返回当前 count 变量的值,最后输出了 1。

但该程序有个问题,如果,我们同时使用多个协程调用 IncCount 函数,那么有可能我们的协程刚要对变量 count 进行加一的操作,此时另一个协程执行了加一,那么就会导致当前的 count 不准确。

这就是并发导致的问题,因此,我们修改程序为如下:

package main import ( "fmt" "sync" ) var( // 计数器 count int // count 变量的互斥锁 countMux sync.Mutex ) // 返回当前计数器的值 func Count()int{ countMux.Lock() defer countMux.Unlock() return count } // 对计数器的值加一 func IncCount(){ countMux.Lock() defer countMux.Unlock() count++ } func main() { fmt.Println("嗨客网(www.haicoder.net)") IncCount() count := Count() fmt.Println("Count =", count) }

程序运行,如下:

13_golang mutex.png

我们修改程序,在 Count 函数返回当前 count 变量值的时候,我们首先,使用 countMux.Lock() 锁住当前 count 变量的值,最后访问结束后,使用 defer 加上 countMux.Unlock() 释放当前的锁。

在 IncCount 函数里面,同样首先,使用 countMux.Lock() 锁住当前 count 变量的值,最后访问结束后,使用 defer 加上 countMux.Unlock() 释放当前的锁。

这样,即使多个协程,同时对我们的变量进行增加和访问操作,因为我们有锁的存在,因此肯定不会出现并发导致变量的值不准确的问题的发生。

Go语言互斥锁教程总结

Go 语言中的 sync 包提供了两种锁类型,分别为:sync.Mutex 和 sync.RWMutex,即互斥锁和读写锁。