go中读写锁(rwmutex)源码解读实现原理

go读写锁的实现原理

1、RWMutex读写锁的概念

读写锁也就是我们所使用的RWMutex,其实是对于go本身的mutex做的一个拓展,当一个goroutine获得了读锁后,其他goroutine同样可以获得读锁,但是不能获得写锁。相反,当一个goroutine获得了写锁,其他goroutine既不能读也不能写,互斥的概念。

2、使用场景

适用于读多写少的情况

3、底层实现

读写锁实现的结构体位于src下的sync包下的rwmutex.go文件中

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
}	

w字段代表着复用了互斥锁

writerSem代表写信号量,用于写等待读

readerSem代表读信号量,用于读等待写

readerCount代表当前执行读的goroutine数量

readerWait代表被阻塞的准备读的goroutine的数量

4、读锁的实现

加读锁

func (rw *RWMutex) RLock() {
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
		// A writer is pending, wait for it.
		runtime_SemacquireMutex(&rw.readerSem, false, 0)
	}
}

首先看这个if方法,为什么要判断小于0呢?

atomic.AddInt32(&rw.readerCount, 1) < 0调用的这个原子方法,目的就是当goroutine加读锁的时候读锁数量+1,如果返回的数量是负数,那么就代表了当前有其他写锁,这个时候就掉用runtime_SemacquireMutex方法休眠当前goroutine,readerSem就记录者这个goroutine。所以要判断是否小于0

释放读锁

// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {
	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
		// Outlined slow-path to allow the fast-path to be inlined
		rw.rUnlockSlow(r)
	}
}

释放读锁的时候就是对readerCount读数量-1即可,如果返回值小于0,就代表着当前有写的操作,这个时候就会调用rUnlockSlow进入慢速通道

什么是慢速通道

func (rw *RWMutex) rUnlockSlow(r int32) {
	// A writer is pending.
	if atomic.AddInt32(&rw.readerWait, -1) == 0 {
		// The last reader unblocks the writer.
		runtime_Semrelease(&rw.writerSem, false, 1)
	}
}

被阻塞的准备读的goroutine数量-1,如果readerWait为0,就表示当前没有goroutine正在准备读,这个时候去唤醒写操作

5、写锁的实现

加写锁

const rwmutexMaxReaders = 1 << 30
// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
func (rw *RWMutex) Lock() {
	// 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_SemacquireMutex(&rw.writerSem, false, 0)
	}
}

写锁的调用就是调用互斥锁w的lock,如果计算之后还是有其他goroutine持有读锁,那么就调用runtime_SemacquireMutex休眠当前的goroutine等待所有的读操作完成, atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders这个操作目的是防止后面的goroutine拿到读锁,阻塞读的作用。

释放写锁

// Unlock unlocks rw for writing. It is a run-time error if rw is
// not locked for writing on entry to Unlock.
//
// As with Mutexes, a locked RWMutex is not associated with a particular
// goroutine. One goroutine may RLock (Lock) a RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() {
	// Announce to readers there is no active writer.
	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
	if r >= rwmutexMaxReaders {
		race.Enable()
		fatal("sync: Unlock of unlocked RWMutex")
	}
	// Unblock blocked readers, if any.
	for i := 0; i < int(r); i++ {
		runtime_Semrelease(&rw.readerSem, false, 0)
	}
	// Allow other writers to proceed.
	rw.w.Unlock()
}

当释放写锁的时候,首先会通过atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)恢复之前的写入的很大的那个负数,然后看当前有多少个读操作在等待,循环唤醒等待读的goroutine

注意:go中的锁不支持可重入锁,若想实现可自定义实现

6、总结

读写锁区分读锁和写锁,而普通的互斥锁不区分,读写锁主要应用在读多写少的场景,既保证了并发读的执行效率,又保证了线程之间的安全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++读写锁(ReadWrite Lock)是一种线程同步机制,用于在多线程环境下实现对共享资源的读写操作。它允许多个线程同时读取共享资源,但只允许一个线程进行写操作。 在C++读写锁的实现通常使用std::shared_mutex类(也称为shared_mutex)来实现。它是C++14标准引入的一个新特性,提供了读写锁的功能。 使用std::shared_mutex时,可以使用两种不同的锁来进行操作: 1. std::shared_lock:用于共享资源的读取操作。多个线程可以同时获取共享锁,并且不会阻塞彼此。 2. std::unique_lock:用于独占资源的写入操作。只允许一个线程获取独占锁,并且其他线程无法获取共享锁。 通过使用这两种不同的锁,可以实现对共享资源的读写操作的并发性。 下面是一个简单的示例代码,演示了如何使用std::shared_mutex实现读写锁: ```cpp #include <iostream> #include <shared_mutex> #include <thread> std::shared_mutex rwMutex; int sharedData = 0; void ReadData() { std::shared_lock<std::shared_mutex> lock(rwMutex); std::cout << "Read data: " << sharedData << std::endl; } void WriteData() { std::unique_lock<std::shared_mutex> lock(rwMutex); sharedData++; std::cout << "Write data: " << sharedData << std::endl; } int main() { std::thread t1(ReadData); std::thread t2(ReadData); std::thread t3(WriteData); t1.join(); t2.join(); t3.join(); return 0; } ``` 在上述示例,三个线程分别执行了读取操作和写入操作。使用std::shared_lock获取共享锁来实现读取操作,使用std::unique_lock获取独占锁来实现写入操作。这样可以保证在写入操作时不会有其他线程同时进行读取或写入操作。 需要注意的是,std::shared_mutex是C++14引入的特性,因此在使用之前,请确保编译器支持C++14标准。如果编译器不支持C++14,也可以使用第三方库或自行实现读写锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值