Go语言互斥锁和读写锁的用法(附带实例)
在 Go 语言提供的 sync 包中有两种锁类型,即互斥锁(sync.Mutex)和读写互斥锁(sync.RWMutex)。
其中,互斥锁比较简单,当一个协程获得互斥锁后,其他协程只能等这个协程释放互斥锁。
读写互斥锁则是经典的单写多读模型。读锁被占用时仅阻止写,但不阻止读。在写锁被占用的情况下,既阻止写,也组织读。
下例演示互斥锁的使用方法。
【实例】让两个协程依次打印数据。
声明互斥锁的全局变量 mutex,定义打印 3~5 的 printData() 函数,分别调用 Lock() 函数和 Unlock() 函数对 printData() 函数进行加锁、解锁处理。
在 main() 函数中,启动并发程序(即 printData() 函数),分别调用 Lock() 函数和 Unlock() 函数对 main() 函数进行加锁、解锁处理,以实现先由 main() 函数占用资源打印 0~2、待资源被释放后再由 printData() 函数打印 3~5 的目的。代码如下:
下例演示读写互斥锁(sync.RWMutex)的使用方法。
【实例 2】如何使用读写互斥锁:
代码如下:
其中,互斥锁比较简单,当一个协程获得互斥锁后,其他协程只能等这个协程释放互斥锁。
读写互斥锁则是经典的单写多读模型。读锁被占用时仅阻止写,但不阻止读。在写锁被占用的情况下,既阻止写,也组织读。
下例演示互斥锁的使用方法。
【实例】让两个协程依次打印数据。
声明互斥锁的全局变量 mutex,定义打印 3~5 的 printData() 函数,分别调用 Lock() 函数和 Unlock() 函数对 printData() 函数进行加锁、解锁处理。
在 main() 函数中,启动并发程序(即 printData() 函数),分别调用 Lock() 函数和 Unlock() 函数对 main() 函数进行加锁、解锁处理,以实现先由 main() 函数占用资源打印 0~2、待资源被释放后再由 printData() 函数打印 3~5 的目的。代码如下:
package main import ( "fmt" "sync" "time" ) var mutex sync.Mutex // 声明互斥锁的全局变量 func printData() { mutex.Lock() // 加锁 for i := 3; i < 6; i+++ { time.Sleep(1 * time.Second) // 每隔 1s 打印一个数字 fmt.Println(i) } mutex.Unlock() // 解锁 } func main() { go printData() // 启动并发程序 mutex.Lock() // 加锁 for i := 0; i < 3; i+++ { time.Sleep(1 * time.Second) // 每隔 1s 打印一个数字 fmt.Println(i) } mutex.Unlock() // 解锁 time.Sleep(6 * time.Second) // 等待并发程序执行完成 }运行结果如下:
0
1
2
3
4
5
下例演示读写互斥锁(sync.RWMutex)的使用方法。
【实例 2】如何使用读写互斥锁:
- 声明表示随机数的全局变量 numRand、读写互斥锁的全局变量 rw,以及同步等待组的全局变量 wg;
- 定义执行读操作的 read() 函数,使用 RLock() 函数和 RUnlock() 函数对其进行加锁、解锁处理,通过设置延时,打印执行读操作后的随机数;
- 定义执行写操作的 write() 函数,使用 Lock() 函数和 Unlock() 函数进行加锁、解锁处理,通过设置延时,打印执行写操作后的随机数;
- 在 main() 函数中,设置同步等待组,分别各执行 3 次 read() 函数和 write() 函数。
代码如下:
package main import ( "fmt" "math/rand" "sync" "time" ) var numRand int // 声明表示随机数的全局变量 var rw sync.RWMutex // 声明读写互斥锁的全局变量 var wg sync.WaitGroup // 声明同步等待组的全局变量 func read(i int) { rw.RLock() // 加锁 time.Sleep(time.Duration(i) * time.Second) // 设置延时 fmt.Printf("执行读操作,数据:%d\n", numRand) rw.RUnlock() // 解锁 wg.Done() } func write(i int) { rw.Lock() // 加锁处理 numRand = rand.Intn(100) // 100以内的随机数 time.Sleep(time.Duration(i) * time.Second) // 设置延时 fmt.Printf(100以内的随机数 fmt.Printf("执行写操作,数据:%d\n", numRand) rw.Unlock() // 解锁处理 wg.Done() } func main() { wg.Add(6) // 设置同步等待组 for i := 1; i < 4; i+++ { go write(i) // 启动 3 次并发程序,即 write() 函数 } for i := 3; i < 4; i+++ { go read(i) // 启动 3 次并发程序,即 read() 函数 } wg.Wait() // 等待同步等待组执行并发程序 }运行结果如下(不唯一):
执行读操作,数据:0
执行写操作,数据:81
执行读操作,数据:81
执行读操作,数据:81
执行写操作,数据:87
执行写操作,数据:47