golang sync.RWMutex

WenruiGe · · 1150 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

什么是sync.RWMutex

首先看一下RWMutex的定义

RWMutex是读/写互斥锁。锁可以由任意数量的读者或单个写者持有。 RWMutex的零值是未锁定的互斥锁

这个package包含了4个方法

func (rw *RWMutex) Lock         // Lock locks rw for writing.
func (rw *RWMutex) RLock        // RLock locks rw for reading.
func (rw *RWMutex) RUnlock      // Unlock locks rw for reading
func (rw *RWMutex) Unlock       // Unlock unlocks rw for writing
复制代码

本次来介绍一下sync.RWMutex的实现,还是先举例

var mu sync.RWMutex
var data map[string]string

func main() {
	data = map[string]string{"hoge": "fuga"}
	mu = sync.RWMutex{}
	go read()
	go read()
	go write()
	go read()
	time.Sleep(5 * time.Second)
}
// 读方法
func read() {
	println("read_start")
	mu.RLock()
	defer mu.RUnlock()
	time.Sleep(1*time.Second)
	println("read_complete", data["hoge"])
}
// 写方法
func write() {
	println("write_start")
	//mu.Lock()       仔细看下这两行代码,此处是注释掉的
	//defer mu.Unlock()
	time.Sleep(2 * time.Second)
	data["hoge"] = "piyo"
	println("write_complete")
}
复制代码

注释掉上述两行代码会输出什么呢?

read_start
read_start
write_start
read_start
read_complete fuga
read_complete fuga
read_complete fuga
write_complete
复制代码

如果把上述代码注释掉的地方打开结果会有影响吗?答案是肯定的!

read_start
write_start
read_start
read_start
read_complete fuga
write_complete
// 可以看到 read_complete已经被write的字串替换掉了
read_complete piyo
read_complete piyo
复制代码

此时就需要讲解下sync.RWMutex锁的实现了,先看下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
}
复制代码

RLock & RUnlock 读锁 & 释放读锁

设定最大的读锁数量 
const rwmutexMaxReaders = 1 << 30

RLock锁定rw进行阅读。
它不应该用于递归读锁定;被阻止的锁调用排除新读者获取锁定。

func (rw *RWMutex) RLock() {

	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
	    // 一个写者正在等待,waiting...
		runtime_SemacquireMutex(&rw.readerSem, false)
	}
	// 静态检测
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
	}
}
复制代码

此处atomic.AddInt32(&rw.readerCount,1)执行的是原子加操作,此时有两种场景:

  • 此时无写锁 (readerCount + 1) > 0 RLock正常结束
  • 此时有写锁 (readerCount + 1) < 0 等待写锁结束
RUnlock撤销单个RLock调用;它不会影响其他同时阅读的读者。
如果在进入RUnlock时没有锁定rw进行读取,则会出现运行时错误。
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()
			// 报错,解锁了一个未锁的RWMutex
			throw("sync: RUnlock of unlocked RWMutex")
		}
		// 如果此时没有读者等待
		if atomic.AddInt32(&rw.readerWait, -1) == 0 {
			// The last reader unblocks the writer.
			runtime_Semrelease(&rw.writerSem, false)
		}
	}
	if race.Enabled {
		race.Enable()
	}
}
复制代码
  • runtime_SemacquireMutex函数是goroutine等待队列的enqueue
  • runtime_Semrelease函数是goroutine等待队列的dequeue操作

atomic.AddInt32(&rw.readerCount, -1)此处也有几种场景 (ps:此处只考虑单个读写锁)

读锁&写锁 写锁 读锁
readerCount -(1<<30) panic 0
readerWait 0 panic 0

Lock & UnLock 写锁&释放写锁

Lock锁定用于写入。如果锁已经锁定以进行读取或写入,则Lock将锁定,直到锁定可用。
func (rw *RWMutex) Lock() {
       // 静态检测
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	
	// 首先,与其他写者解决竞争问题(这里采用了互斥锁)
	rw.w.Lock()
	
	// 如果一个写者得到了锁,readerCount = - (1<<30) , r = 0
	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
	
	// 等待活跃的读者
	// r!=0说明有读者加锁,此处需等待
	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
		runtime_SemacquireMutex(&rw.writerSem, false)
	}
	
	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.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")
	}
	// 循环等待读者完成
	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()
	}
}
复制代码
读锁&写锁 写锁 读锁
readerCount 1 0 panic
readerWait 1 0 panic

that's all


有疑问加站长微信联系(非本文作者)

本文来自:掘金

感谢作者:WenruiGe

查看原文:golang sync.RWMutex

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1150 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传