go-RWLock读写锁实现

package myrwmutex

import (
	"sync/atomic"
	"sync"
)

// go实现的读写锁是写优先,即有写时,会先等待已有的读锁释放,在此写之后的读写操作都必须等此写操作完成
// 而另一种读优先,是指写到达时,之后又有读到达,则如果此时之前的读仍持有锁,则允许继续读操作


type RWMutex struct {
	w           sync.Mutex   // 互斥锁解决多个writer的竞争, 即writer线程间是互斥的
	writerSem   uint32  // writer信号量
	readerSem   uint32  // reader信号量
	readerCount int32   // reader的数量,以及是否有writer竞争锁
	readerWait  int32   // writer等待完成的reader的数量
}
const rwmutexMaxReaders = 1 << 30 // 最大的读者数量


// semaAcquire
func semaAcquire(num *uint32, lifo bool, skipframes int) {

}

// semaRelease
func semaRelease(num *uint32, lifo bool, skipframes int) {

}


// RLock 获取读锁
func (rw *RWMutex) RLock() {
	// 读者数量+1,readerCount为负值表示此时有write等待请求锁,因为writer优先级高,所以把后来的reader阻塞休眠
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
		semaAcquire(&rw.writerSem, false, 0)   // 等待写锁释放
	}
}

// RUnlock 释放读锁
func (rw *RWMutex) RUnlock() {
	if atomic.AddInt32(&rw.readerCount, -1) < 0 {
		rw.rUnlockSlow() // 有等待的writer
	}
}

func (rw *RWMutex) rUnlockSlow() {
	// 将readerWait(表示writer到来时有多少个reader)-1,为0表示所有reader都释放了
	if atomic.AddInt32(&rw.readerWait, -1) == 0 {
		semaRelease(&rw.writerSem, false, 1)
	}
}


// Lock 获取写锁
func (rw *RWMutex) Lock() {
	// 首先解决其他writer竞争问题
	rw.w.Lock()
	// 反转readerCount,表明有writer在竞争锁
	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
	// 如果当前有reader持有锁,那么需要等待
	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
		semaAcquire(&rw.writerSem, false, 1)
	}
}

// Unlock 释放写锁
func (rw *RWMutex) Unlock() {
	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)

	for i := 0; i < int(r); i++ {
		semaRelease(&rw.writerSem, false, 0)
	}
	rw.w.Unlock()
}

//尝试获取
func(rw *RWMutex) TryRLock() {

}

中心思想:writer线程之间互斥,但读和读质检不互斥。
readerCount记录当前有多少个reader,一旦有writer尝试获得锁,此值变为负数(即n-Max),同事将此值记录到readerWait上,则后来的reader观察到此值为负时就等待。
reader释放锁时,readerCount自减,如果为负数则readerWait也自减,当n个reader全部释放后,
readerCount变为n-Max-n即-Max,readerWait为0,则释放writerSem信号量,使等待的writer获得锁返回。
当writer释放锁后,readerCount反转(即r+Max),这个r最开始为-Max,当writer占有锁时,如果有新reader进来,会将r加一,同时发现为负值则等待。假设中间有m个新reader,则writer释放锁时,
readerCount为-Max+m+Max,同时释放m个readerSem 信号量,m个读者同时获得锁。

疑惑点:
这里readerCount反转是通过-Max得到的,那么要求readerCount的数量不超过Max,规定Max为2<<30
readerCount为int32类型,能表示的整数范围在负的2的31次方到正的2的31次方-1,因为有一位是符号位,总的值范围是2的32次方。
查看代码,当readerCount为Max或Max-1或Max-1时,此时来的writer,则readerCount变为0或-1或-2,
则新来的reader第一步先readerCount加一,得到1或者0或者-1,只有结果为负数时才会阻塞休眠,那么1和0的情况会直接获得锁,这种情况是正确的吗?

包内扩展方法

// TryRLock 尝试获取读锁
func(rw *RWMutex) TryRLock() bool {
	// TryRLock失败时不能影响内部的任何值,因此必须用CompareAndSwapInt32函数
	old := atomic.LoadInt32(&rw.readerCount)
	if old < 0 {
		return false
	}
	return atomic.CompareAndSwapInt32(&rw.readerCount, old, old+1)

	//if atomic.AddInt32(&rw.readerCount, 1) < 0 {
	//	return false
	//}
	//return true
}

// IsPendingWriter 是否有writer
func(rw *RWMutex) IsPendingWriter() bool {
	old := atomic.LoadInt32(&rw.readerCount)
	if old < 0 {
		return true
	}
	return true
}

// GetReaderCnt 获取reader数量,包含正在持有锁的reader和后来的reader
func(rw *RWMutex) GetReaderCnt() int32 {
	old := atomic.LoadInt32(&rw.readerCount)
	if old < 0 {
		return old + rwmutexMaxReaders
	}
	return old
}

// GetWaitReaderCnt 获得持有锁的reader
func(rw *RWMutex) GetWaitReaderCnt() int32 {
	old := atomic.LoadInt32(&rw.readerWait)
	return old
}

包外扩展方法

package hacker

import (
	"sync"
	"sync/atomic"
	"unsafe"
)

type RWMutex struct {
	m           sync.RWMutex   // 互斥锁解决多个writer的竞争, 即writer线程间是互斥的
}

const rwmutexMaxReaders = 1 << 30 // 最大的读者数量
// TryRLock
 尝试获取读锁
func(rw *RWMutex) TryRLock() bool {
	// TryRLock失败时不能影响内部的任何值,因此必须用CompareAndSwapInt32函数
	// readerCount前有一个Mutex和两个uint32, 在不可虑结构体对齐的情况下
	readerCountAddr := (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(&rw.m)) + unsafe.Sizeof(sync.Mutex{}) + 2*unsafe.Sizeof(uint32(0))))
	old := atomic.LoadInt32(readerCountAddr)
	if old < 0 {
		return false
	}
	return atomic.CompareAndSwapInt32(readerCountAddr, old, old+1)
}

读写锁使用的问题:
1.读写锁用在什么样的场景?(或读写锁主要用来解决什么问题,说说对读写锁的理解?)
在多读少写的场景下,读写锁有比互斥锁更好的性能。
它允许多个同时读,但不允许同时写或同时读写。
有读优先和写优先两种:
在读持有锁时,新写到来,如果再来的读允许获得锁,则是读优先;如果后来的读要等写完成才可以,则是写优先。读优先可能会产生写的饥饿现象。
2.说说RWMutex的实现原理?说说RWMutex与Mutex的区别?
RWMutex内部利用Mutex实现写和写的互斥,其他则使用readerCount、readerWait来标记reader数量、写等待的reader数量,另外两个信号量用来同步。
每次读时readerCount加一,每次读释放readerCount-1,写到来时会将readerCount反转。读者判断为负值则等待。内部主要用add原子操作。
Mutex用一个state标志一个sema信号量,根据state各个bit位标记是否持有锁、等待锁的请求树、唤醒数、饥饿情况等。主要使用CompareAndSwap原子操作。
3.RWMutex源码看过吗?如果使用Mutex来设计一个RWMutex你有什么思路?
4.在执行Lock,Unlock,Rlock,RUnlock时需要考虑什么问题?
Lock时要将readerCount置为负,并写入readerWait数,同时等待读者全部释放。
Unlock是要将eaderCount置为正,将写期间的读请求从阻塞休眠状态唤醒。
Rlock判断readerCount正负值,负数表示有写准备持有锁,要阻塞休眠等待。
RUnlock是要判断readerCount正负值,负数表示有写准备持有锁,要将readerWait自减,减到0表示所有读锁释放,需要唤醒等待的写请求。
5.使用读写锁时有哪些注意点,如何规避死锁问题?
内部有状态值,因此不可复制
内部使用Mutex,因此不可重入

重入会导致死锁
在reader中调用写操作(它会调用 Lock 方法),会形成死锁
writer 依赖活跃的 reader,活跃的reader依赖新来的reader,新来的reader依赖writer,也死锁。

减少锁的粒度、控制加锁时限、注意加锁顺序。

6.如何监控读写锁的等待情况?你有什么思路?
查看readerCount和readerWait值,readerCount为负表示有写等待,此时readerWait是等待读者释放锁的数量,新来的读者数量是readerCount+Max-readerWait

死锁产生的条件
(1)互斥条件:一个资源每次只能被一个进程使用。

(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS没有提供读写锁的标准实现,但可以通过互斥量和信号量来实现读写锁读写锁是用于读写访问共享资源的一种同步机制,它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。 以下是一个基于FreeRTOS的简单实现: ``` #include "FreeRTOS.h" #include "semphr.h" /* 定义读写锁结构体 */ typedef struct { SemaphoreHandle_t mutex; // 互斥量 SemaphoreHandle_t rw_sem; // 信号量 int readers; // 当前读者数量 } rwlock_t; /* 初始化读写锁 */ void rwlock_init(rwlock_t *rwlock) { rwlock->mutex = xSemaphoreCreateMutex(); rwlock->rw_sem = xSemaphoreCreateBinary(); rwlock->readers = 0; xSemaphoreGive(rwlock->rw_sem); } /* 获取读锁 */ void rwlock_read_lock(rwlock_t *rwlock) { xSemaphoreTake(rwlock->mutex, portMAX_DELAY); rwlock->readers++; if (rwlock->readers == 1) { xSemaphoreTake(rwlock->rw_sem, portMAX_DELAY); } xSemaphoreGive(rwlock->mutex); } /* 释放读锁 */ void rwlock_read_unlock(rwlock_t *rwlock) { xSemaphoreTake(rwlock->mutex, portMAX_DELAY); rwlock->readers--; if (rwlock->readers == 0) { xSemaphoreGive(rwlock->rw_sem); } xSemaphoreGive(rwlock->mutex); } /* 获取写锁 */ void rwlock_write_lock(rwlock_t *rwlock) { xSemaphoreTake(rwlock->rw_sem, portMAX_DELAY); } /* 释放写锁 */ void rwlock_write_unlock(rwlock_t *rwlock) { xSemaphoreGive(rwlock->rw_sem); } ``` 在这个实现中,我们使用了一个互斥量来保证多个线程不会同时访问`readers`计数器。`rw_sem`是一个二元信号量,用于控制读者和写者之间的访问。当有一个写者时,读者需要等待,直到写者释放信号量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值