一、互斥锁
- Go语言中多个协程操作一个变量时会出现冲突的问题
- go run -race 可以查看竞争
- 可以使用sync.Mutex对内容加锁
- 互斥锁的使用场景
- 多个goroutine访问同一个函数(代码段)
- 这个函数操作一个全局变量
- 为了保证共享变量安全性,值合法性
- 使用互斥锁模拟售票窗口
package main
import (
"fmt"
"sync"
"time"
"math/rand"
)
var (
//票数
num = 100
wg sync.WaitGroup
//互斥锁
mu sync.Mutex
)
func sellTicker(i int) {
defer wg.Done()
for {
//加锁,多个goroutine互斥
mu.Lock()
if num >= 1 {
fmt.Println("第", i, "个窗口卖了", num)
num = num - 1
}
//解锁
mu.Unlock()
if num <= 0 {
break
}
//添加休眠,防止结果可能出现在一个goroutine中
time.Sleep(time.Duration(rand.Int63n(1000) * 1e6))
}
}
func main() {
//设置随机数种子
rand.Seed(time.Now().UnixNano())
//计算器的起始值和票数相同
wg.Add(4)
go sellTicker(1)
go sellTicker(2)
go sellTicker(3)
go sellTicker(4)
wg.Wait()
fmt.Println("所有票卖完")
}
二、RWMutex读写锁
RWMutex 源码如下
// There is a modified copy of this file in runtime/rwmutex.go.
// If you make any changes here, see if you should make them there.
// A RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers or a single writer.
// The zero value for a RWMutex is an unlocked mutex.
//
// A RWMutex must not be copied after first use.
//
// If a goroutine holds a RWMutex for reading and another goroutine might
// call Lock, no goroutine should expect to be able to acquire a read lock
// until the initial read lock is released. In particular, this prohibits
// recursive read locking. This is to ensure that the lock eventually becomes
// available; a blocked Lock call excludes new readers from acquiring the
// lock.
type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing readers
}
Go语言标准库中API如下
type RWMutex
func (rw *RWMutex) Lock()//禁止其他协程读写
func (rw *RWMutex) Unlock()
func (rw *RWMutex) RLock()//禁止其他协程写入,只能读取
func (rw *RWMutex) RUnlock()
func (rw *RWMutex) RLocker() Locker
- Go语言中的map不是线程安全的,多个goroutine同时操作会出现错误.
- RWMutex可以添加多个读锁或一个写锁.读写锁不能同时存在.
- map在并发下读写就需要结合读写锁完成
- 互斥锁表示锁的代码同一时间只能有一个人goroutine运行,而读写锁表示在锁范围内数据的读写操作
package main
import (
"fmt"
"sync"
"strconv"
)
func main() {
var rwm sync.RWMutex
m := make(map[string]string)
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func(j int) {
//没有锁在map时可能出现问题
rwm.Lock()
m["key"+strconv.Itoa(j)] = "value" + strconv.Itoa(j)
fmt.Println(m)
rwm.Unlock()
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("程序结束")
}