一、普通锁Mutex
1.1 原型
type Mutex struct {
state int32
sema uint32
}
- Mutex是⼀个互斥锁,可以创建为其他结构体的字段;零值为解锁状态。Mutex类型的锁和线程⽆关,可以由不同的线程加锁和解锁。
1.2 Mutex中的方法
- func (m *Mutex) Lock()
Lock⽅法锁住m,如果m已经加锁,则阻塞直到m解锁。 - func (m *Mutex) Unlock()
Unlock⽅法解锁m,如果m未加锁会导致运⾏时错误。锁和线程无关,可以由不同的线程加锁和解锁。
案例1.用普通锁来实现火车站卖票
//myMutexTick.go
// myMutexDes project main.go
package main
import (
"fmt"
"sync"
"time"
)
//全局变量
var ticks = 100
var wg sync.WaitGroup
var mutex sync.Mutex
func saleTickets(name string, wg *sync.WaitGroup) {
for {
mutex.Lock()
if ticks > 0 {
time.Sleep(200 * time.Microsecond)
fmt.Println(name, ": ", ticks)
ticks--
} else {
fmt.Println(name, "结束卖票...")
mutex.Unlock()
break
}
mutex.Unlock()
}
wg.Done() //通知计数器减一
}
func main() {
//模拟火车站卖票
//火车票一共100张,4个售票口出售(相当于4个子协程)
var wg sync.WaitGroup
wg.Add(4)
go saleTickets("售票口A ", &wg)
go saleTickets("售票口B ", &wg)
go saleTickets("售票口C ", &wg)
go saleTickets("售票口D ", &wg)
wg.Wait()
fmt.Println("所有车票已售空,程序结束!")
}
效果如下:
二、读写锁RWMutex
2.1 原型
type RWMutex struct {
w Mutex
writerSem uint32
readerSem uint32
readerCount int32
readerWait int32
}
- RWMutex是读写互斥锁。该锁可以被同时多个读取者持有或唯⼀个写⼊者持有。 RWMutex可以创建为其他结构体的字段;零值为解锁状态。 RWMutex类型的锁也和线程⽆关,可以由不同的线程加读取锁/写⼊和解读取锁/写⼊锁。
- 锁定的规则:
a) 读写锁的使⽤中:写操作都是互斥的、读和写是互斥的、读和读不互斥。
b) 可以多个goroutine同时读取数据,但只允许⼀个goroutine写数据。
2.2 RWMutex中的方法
- func (rw *RWMutex) Lock()
Lock⽅法将rw锁定为写⼊状态,禁⽌其他线程读取或者写⼊。 - func (rw *RWMutex) Unlock()
Unlock⽅法解除rw的写⼊锁状态,如果m未加写⼊锁会导致运⾏时错误。 - func (rw *RWMutex) RLock()
RLock⽅法将rw锁定为读取状态,禁⽌其他线程写⼊,但不禁⽌读取。 - func (rw *RWMutex) RUnlock()
Runlock⽅法解除rw的读取锁状态,如果m未加读取锁会导致运⾏时错误。 - func (rw *RWMutex) RLocker() Locker
Rlocker⽅法返回⼀个互斥锁,通过调⽤rw.Rlock和rw.Runlock实现了Locker接⼝。
案例2. 主线程加锁、解锁子协程
//myMutexWrite.go
// myMutexWrite project main.go
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var rwm sync.RWMutex
for i := 1; i <= 3; i++ {
go func(i int) {
fmt.Printf("goroutine %d, 尝试读锁定", i)
rwm.RLock()
fmt.Printf("goroutine %d, 已经读锁定了...\n", i)
time.Sleep(3 * time.Second)
fmt.Printf("goroutine %d, 读解锁...\n", i)
rwm.RUnlock()
}(i)
}
time.Sleep(1 * time.Second)
fmt.Println("main 尝试写锁定...")
rwm.Lock()
fmt.Println("main 已经写锁定了...")
rwm.Unlock()
fmt.Printf("main 写解锁...")
}
效果如下: