返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

rwmutex

发布于 2024-10-12 19:15:54 字数 2340 浏览 0 评论 0 收藏 0

写锁独占,须等待读锁全部释放。读之间其实不存在锁。

源码剖析

同样是基于运行时信号量实现。
另有 RLocker 方法返回 读锁Locker 接口,解决方法名不同的问题。

// sync/rwmutex.go

type RWMutex struct {
	w           Mutex  // 写锁,阻止写并发。
	writerSem   uint32 // 写信号量
	readerSem   uint32 // 读信号量
	readerCount int32  // 读者总数,含新人。
	readerWait  int32  // 写手需等待的已锁读者数。
}

加锁

读锁仅增加 读者计数器readerCount+1 )即可,无实质性锁定。
计数可能是负数,因为写手会减去常量值( -Max ),使其变成负数。
目的是阻止新读者取锁进入临界区。只能休眠,等写手解锁时唤醒。

func (rw *RWMutex) RLock() {
    
    // 正常读锁,计数 +1,结果大于零。

    // 负数只能表示写手 -Max 了。
    // 累加计数,休眠,等待写手唤醒。
    
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
		runtime_SemacquireMutex(&rw.readerSem, false, 0)
	}
}

写手 ,占住写锁( w ),阻止其他写手进入。
将读者计数减去常量值( -Max ),阻止新读者取锁进入临界区。
保存当前已锁读者数( raderWait )。休眠,等最后的读者解锁时唤醒。

const rwmutexMaxReaders = 1 << 30

func (rw *RWMutex) Lock() {
    
	// 先占住写锁。
	rw.w.Lock()
    
    // 将读者计数变成负数,阻止新读者进入临界区。
	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + 
         rwmutexMaxReaders

    // 保存要等待的已锁读者数,休眠。
	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
		runtime_SemacquireMutex(&rw.writerSem, false, 0)
	}
}

解锁

读者解锁时,递减读者计数器。
如结果为负数,表示有写手在等待。递减等待计数。最后一位读者解锁,唤醒写手。

func (rw *RWMutex) RUnlock() {
    
    // 如果读计数器是负数,那么是有写手在等待。
	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
		rw.rUnlockSlow(r)
	}
}

func (rw *RWMutex) rUnlockSlow(r int32) {
    
    // 不成对调用,解锁次数过多。
	if r+1 == 0 || r+1 == -rwmutexMaxReaders {
		race.Enable()
		throw("sync: RUnlock of unlocked RWMutex")
	}
    
    // 等待数 -1。最后一位,有责任唤醒写手。
	if atomic.AddInt32(&rw.readerWait, -1) == 0 {
		runtime_Semrelease(&rw.writerSem, false, 1)
	}
}

写手解锁时,将读者计数加上常量值( +Max ),其结果就是休眠读者数量。
唤醒所有休眠读者,释放写锁。

func (rw *RWMutex) Unlock() {
    
	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, 0)
	}
    
	rw.w.Unlock()
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文