golang中的Mutex和RWMutex
Posted zp900704
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang中的Mutex和RWMutex相关的知识,希望对你有一定的参考价值。
????sync中有两种锁 Mutex和RWMutex,Mutex的读和写必须都进行排队,只能一个完成了在进行下一个,RWMutex读可以并行,写只能一个一个进行,当有读时,需要所有的读全部关闭后才能进行写操作,有写
时,需要等写操作完成了才能进行读操作(读并行,写单一)。(sync中有一个map对象是线程安全的,默认的map不是线程安全的)。
Mutex
先看下面的代码:
package main
import (
"fmt"
"sync"
"time"
)
func main()
go setm(1)
time.Sleep(time.Millisecond * 1)
go setm(2)
time.Sleep(time.Millisecond * 1)
go setm(3)
time.Sleep(time.Millisecond * 1)
go setm(4)
time.Sleep(time.Millisecond * 1)
time.Sleep(time.Minute)
运行结果:
---- 2019-06-11 15:55:15.053098 +0800 CST m=+0.001952900 setm 1
---- 2019-06-11 15:55:15.0630003 +0800 CST m=+0.011855100 setm1 1
---- 2019-06-11 15:55:15.0550915 +0800 CST m=+0.003946400 setm 2
---- 2019-06-11 15:55:15.0589531 +0800 CST m=+0.007807900 setm 4
---- 2019-06-11 15:55:15.0569997 +0800 CST m=+0.005854500 setm 3
---- 2019-06-11 15:55:16.0637775 +0800 CST m=+1.012632400 setm1 2
---- 2019-06-11 15:55:17.0644006 +0800 CST m=+2.013255400 setm1 4
---- 2019-06-11 15:55:18.0651814 +0800 CST m=+3.014036200 setm1 3
从上面的输出可以看到,lock的后的代码是阻塞的,像队列一样,先进去,先出来,一个接一个完成。
RWMutex
这是一个读写锁,可以并发的读,单一的写,主要用于读多写少的情况下。
先看下面的一段代码:
package main
import (
"fmt"
"sync"
"time"
)
var data = map[string]int
"a": 0,
var lock sync.RWMutex
func main()
go get(1)
time.Sleep(time.Millisecond * 1)
go get(2)
time.Sleep(time.Millisecond * 1)
go set(1)
time.Sleep(time.Millisecond * 1)
go set(2)
time.Sleep(time.Millisecond * 1)
go get(3)
time.Sleep(time.Millisecond * 1)
go set(3)
time.Sleep(time.Millisecond * 1)
go set(4)
time.Sleep(time.Millisecond * 1)
go get(4)
time.Sleep(time.Minute)
func get(index int)
fmt.Println("----", time.Now(), "get", index, data["a"])
lock.RLock()
defer lock.RUnlock()
fmt.Println(time.Now(), "get", data["a"])
time.Sleep(time.Second * 1)
func set(a int)
fmt.Println("----", time.Now(), "set", a, data["a"])
lock.Lock()
defer lock.Unlock()
fmt.Println(time.Now(), "set", a)
data["a"] = a
time.Sleep(time.Second * 1)
运行结果:
---- 2019-06-11 15:59:54.1919927 +0800 CST m=+0.002940900 get 1 0
2019-06-11 15:59:54.2015089 +0800 CST m=+0.012457100 get 0
---- 2019-06-11 15:59:54.1936932 +0800 CST m=+0.004641400 get 2 0
2019-06-11 15:59:54.2015089 +0800 CST m=+0.012457100 get 0
---- 2019-06-11 15:59:54.1956461 +0800 CST m=+0.006594300 set 1 0
---- 2019-06-11 15:59:54.1966613 +0800 CST m=+0.007609600 set 2 0
---- 2019-06-11 15:59:54.1985726 +0800 CST m=+0.009520800 get 3 0
---- 2019-06-11 15:59:54.200525 +0800 CST m=+0.011473200 set 3 0
---- 2019-06-11 15:59:54.2023586 +0800 CST m=+0.013306800 set 4 0
---- 2019-06-11 15:59:54.2036319 +0800 CST m=+0.014580200 get 4 0
2019-06-11 15:59:55.2027123 +0800 CST m=+1.013660500 set 1
2019-06-11 15:59:56.202904 +0800 CST m=+2.013852200 get 1
2019-06-11 15:59:56.202904 +0800 CST m=+2.013852200 get 1
2019-06-11 15:59:57.2032109 +0800 CST m=+3.014159100 set 2
2019-06-11 15:59:58.2037928 +0800 CST m=+4.014741000 set 3
2019-06-11 15:59:59.2046672 +0800 CST m=+5.015615500 set 4
????从运行结果可以看到,同时可以有多个读,但是只能有一个写,当有写时,读会阻塞。
从上面的运行结果看,有一点很奇怪,明明是两个连写的写操作,在进行读操作,但是,只进行了一个写操作后,就进行了所有的读操作然后在进行了剩余的写操作。
我们看一下golang中对RWMutex的实现:
/*读写锁的定义*/
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
func (rw *RWMutex) RLock()
if race.Enabled
_ = rw.w.state
race.Disable()
if atomic.AddInt32(&rw.readerCount, 1) < 0
// A writer is pending, wait for it.
runtime_Semacquire(&rw.readerSem)
if race.Enabled
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
func (rw *RWMutex) RUnlock()
if race.Enabled
_ = rw.w.state
race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
race.Disable()
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0
if r+1 == 0 || r+1 == -rwmutexMaxReaders
race.Enable()
throw("sync: RUnlock of unlocked RWMutex")
// A writer is pending.
if atomic.AddInt32(&rw.readerWait, -1) == 0
// The last reader unblocks the writer.
runtime_Semrelease(&rw.writerSem, false)
if race.Enabled
race.Enable()
func (rw *RWMutex) Lock()
if race.Enabled
_ = rw.w.state
race.Disable()
// First, resolve competition with other writers.
rw.w.Lock()
// Announce to readers there is a pending writer.
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0
runtime_Semacquire(&rw.writerSem)
if race.Enabled
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
race.Acquire(unsafe.Pointer(&rw.writerSem))
func (rw *RWMutex) Unlock()
if race.Enabled
_ = rw.w.state
race.Release(unsafe.Pointer(&rw.readerSem))
race.Release(unsafe.Pointer(&rw.writerSem))
race.Disable()
// Announce to readers there is no active writer.
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders
race.Enable()
throw("sync: Unlock of unlocked RWMutex")
// Unblock blocked readers, if any.
for i := 0; i < int(r); i++
runtime_Semrelease(&rw.readerSem, false)
// Allow other writers to proceed.
rw.w.Unlock()
if race.Enabled
race.Enable()
读锁定(RLock):
当存在写操作时,进行阻塞,等待写信号完成然后进行读操作。如果没有写操作,则执读
读解锁(RUnlock):
读解锁:读解锁,触发读完成操作
写锁定(Lock):
先进行lock操作,进行代码阻塞,然后判断是否存在读操作,当存在读操作时,进行等待,等所有的读操作完成后在进行剩余的操作 写操作完成后,出写操作完成
写解锁(Unlock):
先出发写完成操作,然后在执行写解锁
对上面的代码进行分析:
先执行两次读取操作,此时,读还没有完成。由于是并发,此时执行写操作,代码锁定,等待读操作完成,此时,读操作数量是2,然后执行set(2)操作,由于lock了,此时代码阻塞,只能等set(1)解锁。后面的读取操作,由于有前置的写操作,需要等待写操作完成,由于set(1)执行完,解锁前先出发runtime_Semrelease,此时,执行读操作,读操作完成后,执行解锁操作,执行剩余的赋值操作
go get(1) //获取数据,read+1=1
time.Sleep(time.Millisecond * 1)
go get(2) //获取数据, read+1=2
time.Sleep(time.Millisecond * 1)
go set(1) //锁定 等待read完成:get(1)、get(2)
time.Sleep(time.Millisecond * 1)
go set(2) //代码锁定 等待set(1)完成
time.Sleep(time.Millisecond * 1)
go get(3) //获取数据,等待写操作完成即:set(1)完成,
time.Sleep(time.Millisecond * 1)
go set(3) //设置数据,等待set(2) 完成
time.Sleep(time.Millisecond * 1)
go set(4) //设置数据,等待set(2) 完成
time.Sleep(time.Millisecond * 1)
go get(4) //获取数据,等待写操作完成即:set(1)完成,
time.Sleep(time.Minute)
以上是关于golang中的Mutex和RWMutex的主要内容,如果未能解决你的问题,请参考以下文章
Golang 基础:底层并发原语 Mutex RWMutex Cond WaitGroup Once等使用和基本实现
Golang 基础:底层并发原语 Mutex RWMutex Cond WaitGroup Once等使用和基本实现