Linux 锁机制(2)之读写锁 rwlock_t
1. 读写锁特性
读写锁的特性为:写独占,读共享;写锁优先级高。
对于读写锁,掌握了这12个字就足矣了。
2. 写独占,读共享;写锁优先级高 解析
- 读写锁是“写模式加锁”时, 解锁前,所有尝试对该锁进行加锁(不管是读锁还是写锁)的线程都会被阻塞;–> 写独占
- 读写锁是“读模式加锁”时, 如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。–> 读共享
- 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求,优先满足写模式锁。–> 写锁优先级高
3. 适用场景
读写锁非常适合于对数据结构读多写少的情况。
4. 读写锁API
4.1 Linux线程相关API
pthread_rwlock_init函数
pthread_rwlock_destroy函数
pthread_rwlock_rdlock函数
pthread_rwlock_wrlock函数
pthread_rwlock_tryrdlock函数
pthread_rwlock_trywrlock函数
pthread_rwlock_unlock函数
4.2 系统相关API
方法 描述
rwlock_init() 初始化指定的rwlock_t
read_lock() 获取指定的读锁
read_unlock() 释放指定的读锁
write_lock() 获得指定的写锁
write_unlock() 释放指定的写锁
write_trylock() 试图获得指定的写锁;如果写锁不可用,返回非0值
read_lock_irq() 禁止本地中断并获得指定读锁
read_unlock_irq() 释放指定的读锁并激活本地中断
read_lock_irqsave() 存储本地中断的当前状态,禁止本地中断并获得指定读锁
read_unlock_irqrestore() 释放指定的读锁并将本地中断恢复到指定前的状态
write_lock_irq() 禁止本地中断并获得指定写锁
write_unlock_irq() 释放指定的写锁并激活本地中断
write_lock_irqsave()存储本地中断的当前状态,禁止本地中断并获得指定写锁
write_unlock_irqrestore() 释放指定的写锁并将本地中断恢复到指定前的状态
5. 读写锁缺点及解决
读写锁的缺点(写独占时不可读)
RCU 是对读写锁的优化/替换,解决读写互斥问题(随时读,写互斥)
具体参考:
Linux RCU机制
https://blog.csdn.net/lqy971966/article/details/118993557
6. 代码例子
/* 2个线程不定时 "写" 全局资源,3个线程不定时 "读" 同一全局资源 */
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#define COUNT_OF_READ 3
#define COUNT_OF_WRITE 2
int g_iCounter; //全局资源
pthread_rwlock_t g_iRwLock;
void *th_write(void *arg)
{
int iTmpCount = 0;
int iTmpArg = (int)(int*)arg;
while (1) {
iTmpCount = g_iCounter;
usleep(1000);
pthread_rwlock_wrlock(&g_iRwLock);
printf("=====write %d: %lu: g_iCounter=%d ++g_iCounter=%d\n", iTmpArg, pthread_self(), iTmpCount, ++g_iCounter);
/* pthread_self 返回线程ID */
pthread_rwlock_unlock(&g_iRwLock);
usleep(5000);
}
return NULL;
}
void *th_read(void *arg)
{
int iTmpArg = (int)(int*)arg;
while (1) {
pthread_rwlock_rdlock(&g_iRwLock);
printf("-----read %d: %lu: %d\n", iTmpArg, pthread_self(), g_iCounter);
pthread_rwlock_unlock(&g_iRwLock);
usleep(900);
}
return NULL;
}
int main(void)
{
int iCount = 0;
pthread_t tid[COUNT_OF_WRITE + COUNT_OF_READ];
pthread_rwlock_init(&g_iRwLock, NULL);
for (iCount = 0; iCount < COUNT_OF_WRITE; iCount++)
pthread_create(&tid[iCount], NULL, th_write, (void *)iCount);
for (iCount = 0; iCount < COUNT_OF_READ; iCount++)
pthread_create(&tid[iCount+3], NULL, th_read, (void *)iCount);
for (iCount = 0; iCount < COUNT_OF_WRITE + COUNT_OF_READ; iCount++)
pthread_join(tid[iCount], NULL);
pthread_rwlock_destroy(&g_iRwLock); //释放读写琐
return 0;
}
结果:
root@localhost home]# gcc rwlock.c -lpthread ^
[root@localhost home]# ./a.out > rrr
^C
[root@localhost home]# head -n 30 rrr
-----read 0: 140041912997632: 0
-----read 1: 140041904604928: 0
-----read 2: 140041896212224: 0
=====write 0: 140041929783040: g_iCounter=0 ++g_iCounter=1
-----read 2: 140041896212224: 1
-----read 1: 140041904604928: 1
=====write 1: 140041921390336: g_iCounter=0 ++g_iCounter=2
-----read 0: 140041912997632: 2
-----read 1: 140041904604928: 2
-----read 2: 140041896212224: 2
-----read 0: 140041912997632: 2
-----read 2: 140041896212224: 2
-----read 1: 140041904604928: 2
-----read 0: 140041912997632: 2
-----read 1: 140041904604928: 2
-----read 2: 140041896212224: 2
-----read 0: 140041912997632: 2
-----read 2: 140041896212224: 2
-----read 1: 140041904604928: 2
-----read 0: 140041912997632: 2
=====write 0: 140041929783040: g_iCounter=2 ++g_iCounter=3
=====write 1: 140041921390336: g_iCounter=2 ++g_iCounter=4
-----read 2: 140041896212224: 4
-----read 1: 140041904604928: 4
-----read 0: 140041912997632: 4
-----read 2: 140041896212224: 4
-----read 1: 140041904604928: 4
-----read 0: 140041912997632: 4
-----read 1: 140041904604928: 4
-----read 0: 140041912997632: 4
[root@localhost home]#
代码优化了一下 参考:
https://blog.csdn.net/yychuyu/article/details/84677883