[TOC] # 简介 互斥锁本质是当一个协程访问的时候,其他协程都不能访问. 其实主要是想:修改数据要同步,这样其他协程才可以感知到,所以真正的互斥应该是读取和修改,修改和修改之间,读和读是没有互斥操作的必要的 读写锁可以让多个读并发,但是对于写是互斥的. 当一个协程在写的时候,其他协程不能读也不能写 **同时只能存在写锁定或读锁定(读和写互斥)** go中的读写锁由结构类型`sync.RWMutex`表示.这个类型的方法集合中包含两对方法 一组是对写操作的锁定和解锁,简称:写锁定和写解锁 ~~~ func (*RWMutex) Lock() func (*RWMutex) UnLock() ~~~ 另一组表示对读操作的锁定和解锁,简称为读锁定和读解锁 ~~~ func (*RWMutex) RLock() func (*RWMutex) RUlock() ~~~ 读时共享,写时独占. **写优先级比读高** 读锁也算个锁,锁只能有一把 ~~~ package main import ( "fmt" "sync" ) func main() { var mutex sync.Mutex fmt.Printf("%+v\n", mutex) mutex.Lock() fmt.Printf("%+v\n", mutex) mutex.Unlock() fmt.Printf("%+v\n", mutex) } ~~~ 输出 ~~~ {state:0 sema:0} {state:1 sema:0} {state:0 sema:0} ~~~ 可以看出当Lock()时,state为1,Unlock()时,state为0。 # 使用 读写锁,最好不要和channel一起使用会造成死锁 ~~~ //创建读写锁 var mx sync.RWMutex //读 func readGo(in <-chan int, idx int) { for { //mx.RLock() //读锁 //要求写端同时在线,自己阻塞 num := <-in fmt.Println("读取: ", num, idx) time.Sleep(time.Millisecond * 300) //放大实验现象 //mx.RUnlock() } } //写 func writeGo(out chan<- int, idx int) { for { //生成随机数 num := rand.Intn(1000) mx.Lock() //写锁 out <- num fmt.Println("------写入: ", num, idx) time.Sleep(time.Millisecond * 30) //放大实验现象 mx.Unlock() } } func main() { //播种随机数 rand.Seed(time.Now().UnixNano()) ch := make(chan int) //数据传递的channel for i := 0; i < 5; i++ { go writeGo(ch, i+1) } for i := 0; i < 5; i++ { go readGo(ch, i+1) } for { ; } } ~~~ # 全局变量数据同步 ~~~ //创建读写锁 var mx sync.RWMutex var i int //全局变量模拟共享数据 //读 func readGo(idx int) { for { mx.RLock() //读锁 num := i fmt.Println("读取: ", num, idx) //time.Sleep(time.Millisecond * 30) //放大实验现象 mx.RUnlock() } } //写 func writeGo(idx int) { for { mx.Lock() //写锁 //生成随机数 num := rand.Intn(1000) i = num fmt.Println("------写入: ", num, idx) time.Sleep(time.Millisecond * 30) //放大实验现象 mx.Unlock() } } func main() { //播种随机数 rand.Seed(time.Now().UnixNano()) for i := 0; i < 5; i++ { //5个读过程 go readGo(i+1) } for i := 0; i < 5; i++ { //5个写过程 go writeGo(i+1) } for { ; } } ~~~