读写锁之pthread_rwlock和内核rwlock自旋读写锁
# Linux 锁机制(2)之读写锁 rwlock_t
https://blog.csdn.net/lqy971966/article/details/103541567 (opens new window)
# 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函数
1
2
3
4
5
6
7
2
3
4
5
6
7
# 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() 释放指定的写锁并将本地中断恢复到指定前的状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 5. 代码例子
/* 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;
}
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
结果:
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]#
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
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
代码优化了一下 参考:
https://blog.csdn.net/yychuyu/article/details/84677883
# 参考:
[1. https://cloud.tencent.com/developer/news/308874][1. https_cloud.tencent.com_developer_news_308874]
# 存在读锁时加写锁测试
如果多个线程循环读,即便每个线程会间歇释放读锁,但是若总体一直不释放读锁,则写锁pthread_rwlock_wrlock()将无法获取成功。
可以考虑synchronize_rcu这种优化过的读写锁。
修改代码如下,刚开始写线程可以写,后面读线程轮流占有锁,写线程将再也无法获取到锁。
rwlock_test.cpp
/* 2个线程不定时 "写" 全局资源,5个线程不定时 "读" 同一全局资源 */
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#define COUNT_OF_READ 5
#define COUNT_OF_WRITE 2
int g_iCounter; //全局资源
pthread_rwlock_t g_iRwLock;
void *th_write(void *arg)
{
int iTmpCount = 0;
size_t iTmpArg = (size_t)(int*)arg;
while (1) {
iTmpCount = g_iCounter;
usleep(200000);
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 */
usleep(100000);
pthread_rwlock_unlock(&g_iRwLock);
}
return NULL;
}
void *th_read(void *arg)
{
size_t iTmpArg = (size_t)(int*)arg;
while (1) {
usleep(900);
pthread_rwlock_rdlock(&g_iRwLock);
printf("-----read %lu: %lu: %d\n", iTmpArg, pthread_self(), g_iCounter);
usleep(1000000);
pthread_rwlock_unlock(&g_iRwLock);
}
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++) {
usleep(300000);
pthread_create(&tid[iCount], NULL, th_write, reinterpret_cast<void *>(iCount));
}
for (iCount = 0; iCount < COUNT_OF_READ; iCount++) {
usleep(300000);
pthread_create(&tid[iCount+3], NULL, th_read, reinterpret_cast<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;
}
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
编译:
g++ -o a.out -pthread rwlock_test.cpp
1
输出:
$ ./a.out
=====write 0: 140659470046976: g_iCounter=0 ++g_iCounter=1
=====write 1: 140659461654272: g_iCounter=1 ++g_iCounter=2
=====write 0: 140659470046976: g_iCounter=1 ++g_iCounter=3
-----read 0: 140659453261568: 3
-----read 1: 140659444868864: 3
-----read 2: 140659436476160: 3
-----read 3: 140659428083456: 3
-----read 0: 140659453261568: 3
-----read 4: 140659419690752: 3
-----read 1: 140659444868864: 3
-----read 2: 140659436476160: 3
-----read 3: 140659428083456: 3
-----read 0: 140659453261568: 3
-----read 4: 140659419690752: 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 内核中的自旋读写锁
这个内核同步专栏系列文章貌似不错: https://blog.csdn.net/zhoutaopower/category_11121586.html (opens new window)
- 2.内核自旋锁 https://stephenzhou.blog.csdn.net/article/details/86598839 (opens new window)
- 3.内核自旋读写锁 https://stephenzhou.blog.csdn.net/article/details/86605987 (opens new window)
- 4.顺序锁 https://stephenzhou.blog.csdn.net/article/details/86611798 (opens new window)
- 5.信号量 https://stephenzhou.blog.csdn.net/article/details/86614945 (opens new window)
- 6.互斥体 https://stephenzhou.blog.csdn.net/article/details/86627438 (opens new window)
- 7.RCU机制 https://stephenzhou.blog.csdn.net/article/details/86646688 (opens new window)
编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54