互斥锁是一种常用的控制共享资源访问的方法,它能够保证同时只有一个goroutine可以访问共享资源。Go语言中使用sync包的Mutex类型来实现互斥锁。 定义一个锁:
var lock sync.Mutex
加锁:
lock.Lock()
解锁:
lock.Unlock()
完整示例
package main
import (
"fmt"
"sync"
)
func main() {
var lock sync.Mutex
var wg sync.WaitGroup
x := 0
// 开100个goroutine
for i := 0; i < 100; i++ {
// 计数
wg.Add(1)
go func() {
// 锁了之后其它goroutine不能对x进行操作
lock.Lock()
for j := 0; j < 100; j++ {
x += 1
}
// 解锁
lock.Unlock()
// 执行完毕
wg.Done()
}()
}
// 等待所有goroutine执行完毕
wg.Wait()
// 输出x(10000)
fmt.Println(x)
}
互斥锁是完全互斥的,但是有很多实际的场景下是读多写少的,当并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包中的RWMutex类型。 读写锁分为两种:读锁和写锁。当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁会继续获得锁,如果是获取写锁就会等待;当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待。 定义一个读写锁:
var rwlock sync.RWMutex
加写锁:
rwlock.Lock()
解写锁:
rwlock.Unlock()
加读锁:
rwlock.RLock()
解读锁:
rwlock.RUnlock()
完整示例
package main
import (
"fmt"
"sync"
)
func main() {
var rwlock sync.RWMutex
var wg sync.WaitGroup
x := 0
wg.Add(1)
// 读x
go func() {
for a := 0; a < 100; a++ {
// 读锁
rwlock.RLock()
fmt.Println(x)
// 解读锁
rwlock.RUnlock()
}
wg.Done()
}()
// 开100个goroutine写入x
for i := 0; i < 100; i++ {
// 计数
wg.Add(1)
go func() {
// 写锁
rwlock.Lock()
for j := 0; j < 100; j++ {
x += 1
}
// 解写锁
rwlock.Unlock()
// 执行完毕
wg.Done()
}()
}
// 等待所有goroutine执行完毕
wg.Wait()
}
运行后可发现每次读操作输出的结果,都相差100,因为写锁在解开前不能进行读操作,因此只有等x加上100后才能读,而读锁在解开前不能进行写操作,因此每次读的时候x不能修改。