读写锁
介绍
- 读写锁允许更高的并行性,也叫共享互斥锁。互斥量要么是加锁状态,要么就是解锁状态,而且一次只有一个线程可以对其加锁。读写锁可以有3种状态:
读模式下加锁状态、写模式加锁状态、不加锁状态
。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁,即允许多个线程读但只允许一个线程写。 - 当读操作较多,写操作较少时,可用读写锁提高线程读并发性
具体实现:
以pthread_rwlock_rdlock为例,其他同理
实际有个互斥锁,保护读写锁的数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//arm64
typedef union
{
struct
{
int __lock;
unsigned int __nr_readers;
unsigned int __readers_wakeup;
unsigned int __writer_wakeup;
unsigned int __nr_readers_queued;
unsigned int __nr_writers_queued;
int __writer;
int __shared;
unsigned long int __pad1;
unsigned long int __pad2;
unsigned int __flags;
} __data;
char __size[__SIZEOF_PTHREAD_RWLOCK_T];
long int __align;
} pthread_rwlock_t;
获取读锁:
x86:
1
2
//nptl/sysdeps/unix/sysv/linux/x86_64/pthread_rwlock_rdlock.S
//逻辑与arm64的一致
arm64:
基于glibc-2.17的代码分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//nptl/pthread_rwlock_rdlock.c
/*
* 1 锁住rwlock.data.lock互斥锁
* 2 判断当前是否有写者,实现不同路径
* 1> 如果没有写者,并且是读者优先,即使有写者在等锁,也优先把锁给读者;
---->结束
* 2> 如果有写者,释放rwlock.data.lock互斥锁, 阻塞等待写者释放锁后的唤醒。
* ----->获取rwlock.data.lock互斥锁, 重新获取读锁
*/
/* Acquire read lock for RWLOCK. */
int
__pthread_rwlock_rdlock (rwlock)
pthread_rwlock_t *rwlock;
{
...
/* Make sure we are alone. */
lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);
while (1) {
/* Get the rwlock if there is no writer... */
if (rwlock->__data.__writer == 0
/* ...and if either no writer is waiting or we prefer readers. */
&& (!rwlock->__data.__nr_writers_queued
|| PTHREAD_RWLOCK_PREFER_READER_P (rwlock))) {
++rwlock->__data.__nr_readers; //增加读者
break; //结束
}
++rwlock->__data.__nr_readers_queued; //读写入等待队列
/* Free the lock. */
lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);
/* Wait for the writer to finish. */
lll_futex_wait (&rwlock->__data.__readers_wakeup, waitval,
rwlock->__data.__shared);
/* Get the lock. */
lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);
--rwlock->__data.__nr_readers_queued; //读者等待队列自减,再重新去获取锁
}
lll_unlock(rwlock->__data.__lock);
}
1
2
3
4
5
6
//nptl/pthread_rwlock_wrlock.c
/* Acquire write lock for RWLOCK. */
int
__pthread_rwlock_wrlock (rwlock)
pthread_rwlock_t *rwlock;
{
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//nptl/pthread_rwlock_unlock.c
/* Unlock RWLOCK. */
int
__pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
{
lll_lock (rwlock->__data.__lock, rwlock->__data.__shared); //获取rwlock数据保护的互斥锁
if (rwlock->__data.__writer) //判断是读者 or 写者
rwlock->__data.__writer = 0; //写着释放, 将写者的线程号
else
--rwlock->__data.__nr_readers;//读者自减
if (rwlock->__data.__nr_readers == 0) //判断是否还有读者? 两种情况:1、 写者持锁,当前释放,存在读者等待 2、读者持锁,所有读者都释放锁,存在写者等待
{ //没有读者
if (rwlock->__data.__nr_writers_queued) //判断是否有写者等待?
{
++rwlock->__data.__writer_wakeup; //存在读者等待,唤醒写者
lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);
lll_futex_wake (&rwlock->__data.__writer_wakeup, 1,
rwlock->__data.__shared); //唤醒一个写者,写者抢锁
return 0;
}
else if (rwlock->__data.__nr_readers_queued) //此时写者unlock,存在读者等待。
{
++rwlock->__data.__readers_wakeup; //唤醒读者
lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);
lll_futex_wake (&rwlock->__data.__readers_wakeup, INT_MAX,
rwlock->__data.__shared); //唤醒所有读者,并发读
return 0;
}
}
lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared); //释放rwlock数据保护的互斥锁
return 0;
}
性能测试
测试方案:
总结
对锁性能来看:
1、