Linux线程读写锁pthread_rwlock详解


Creative Commons LicenseCreative Commons LicenseCreative Commons License

本文介绍什么是线程锁,线程锁pthread_rwlock的使用场景,以及如何使用等知识点

1 什么是线程读写锁

Linux中读写锁是一种线程的同步机制,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。读写锁加锁中读者写者的操作规则是不同的:

1)只要没有写模式下的加锁,任意线程都可以进行读模式下的加锁

这个其实可以很好理解,因为单纯的读操作不改变访问的任何资源,多个线程都可以同时无影响的读取资源。

2) 只有读写锁处于不加锁状态时,才能进行写模式下的加锁

读写锁也称为共享-(shared-exclusive)独占锁,当读写锁以读模式加锁时,它是以共享模式锁住(即任何访问者都可以使用),当以写模式加锁时,它是以独占模式锁住(只有获得锁的人可以使用)。

2 读写锁适用场景

读数据的频率远大于写数据的频率的应用中。这样可以在任何时刻运行多个读线程并发的执行,给程序带来了更高的并发度。

3 与互斥锁的区别

1)互斥锁会把试图进入已保护的临界区的线程都阻塞,即同时只有一个线程持有锁

2)读写锁会根据当前进入临界区的线程是读还是写的属性来判断是否允许线程进入。

多个读者可以同时持有锁。

4 读写锁接口

基于POSIX线程库(libpthread, -lpthread)的接口如下

4.1 读写锁的初始化和销毁

1
2
3
4
5
6
7
8
9
10
11
12
int pthread_rwlock_init(pthread_rwlock_t * restrict lock,
const pthread_rwlockattr_t * restrict attr);
pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;
int pthread_rwlock_destroy(pthread_rwlock_t *lock);
pthread_rwlock_init()
/* 用于初始化读写锁,并可以设置读写锁属性,
* 一般默认为NULL,如果要修改写锁属性可以详细参考链接
* http://docs.oracle.com/cd/E19455-01/806-5257/6je9h032t/index.html
* PTHREAD_RWLOCK_INITIALIZER宏定义可以用来静态的分配初始一个读写锁,不需要错误检查,分配默认
* 的读写锁属性,和pthread_rwlock_init()指定NULL属性的效果是一致的。pthread_rwlock_destroy()
* 用于销毁读写锁。
*/

4.2 读写锁的读加锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int pthread_rwlock_rdlock(pthread_rwlock_t *lock);
int pthread_rwlock_timedrdlock(pthread_rwlock_t * restrict lock,
const struct timespec * restrict abstime);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *lock);
/* 读写锁读加锁有3个函数接口,pthread_rwlock_rdlock()请求一个读锁,
* 如果当前没有写线程持有锁或者没有写线程阻塞在取锁时则可以立刻获取到锁。否则请求锁的线程阻塞等
* 待,pthread_rwlock_timedrdlock() 执行同样的读加锁操作,只是有个超时时间,在指定abstime时间
*(超时指定的是绝对时间,不是相对时间)获取不到直接返回,其中abstime的类型为struct timespec
*/
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};

/* pthread_rwlock_tryrdlock()执行同样加锁操作的非阻塞函数,获不到锁立即返回。
注:一个线程可能获取到多个并发的读锁,如果是这样的话,对每个加锁都要释放一次。
*/

4.3 读写锁的写加锁

1
2
3
4
5
6
7
int pthread_rwlock_wrlock(pthread_rwlock_t *lock);
int pthread_rwlock_timedwrlock(pthread_rwlock_t * restrict lock,
const struct timespec * restrict abstime);
int pthread_rwlock_trywrlock(pthread_rwlock_t *lock);
pthread_rwlock_wrlock() //阻塞的形式获取一个写锁
pthread_rwlock_timedwrlock() //执行相同的取写锁的操作, 只是有个超时时间,在指定abstime绝度时间内获取不到直接返回,
pthread_rwlock_trywrlock() //执行同样加锁操作的非阻塞函数,获不到锁立即返回。

4.4 读写所的解锁

1
2
int pthread_rwlock_unlock(pthread_rwlock_t *lock);
// 改函数用来释放获取到的读写锁。

4.5 接口的返回值

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
正确的返回0,各个接口错误的返回值如下定义:
pthread_rwlock_init()
[EAGAIN] 系统缺少资源来初始化读写锁
[EBUSY] 尝试初始化一个已经初始化但还未销毁的锁.
[EINVAL] attr不可用.
[ENOMEM] 内存资源不足
pthread_rwlock_destroy()
[EBUSY] 尝试销毁一个还锁着的锁
[EINVAL] 指定的锁参数不可用
pthread_rwlock_tryrdlock()
[EBUSY] 其写锁正被持有或者写锁阻塞等待
pthread_rwlock_timedrdlock()
[ETIMEDOUT] 加锁超时
pthread_rwlock_rdlock(), pthread_rwlock_timedrdlock(), and pthread_rwlock_tryrdlock()
[EAGAIN] 获取锁的数量已经超过最大值
[EDEADLK] 当前线程已经持有写锁
[EINVAL] 指定的锁参数不可用
The pthread_rwlock_trywrlock()
[EBUSY] 不能非阻塞的获得锁
pthread_rwlock_timedwrlock()
[ETIMEDOUT] 加锁超时
pthread_rwlock_wrlock(), pthread_rwlock_timedwrlock(), and pthread_rwlock_trywrlock()
[EDEADLK] 调用的线程已经持有读写锁了
[EINVAL] 指定的锁参数不可用
pthread_rwlock_unlock()
[EINVAL] 指定的锁参数不可用
[EPERM] 当前线程没有持有锁

5 读写锁简单的实例测试

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>

#define ASIZE 30
#define READ_THREAD_NUM 3
static pthread_rwlock_t arealock;


int locked_area[ASIZE];
int write_changed = 0;
int finished = 0;

void *thread_read(void *arg)
{
int theadid = *(int *)arg;
int index_start = theadid*(ASIZE/READ_THREAD_NUM);
int index_end = index_start + ASIZE/READ_THREAD_NUM;
int ii;
while (!finished) {
if (!write_changed) {
sleep(1);
continue;
}
pthread_rwlock_rdlock(&arealock);
for (ii = index_start; ii < index_end; ii++)
printf("thread%d locked_area[%d]=%d\n", theadid,
ii, locked_area[ii]);
pthread_rwlock_unlock(&arealock);
sleep(1);
write_changed = 0;
}
}

void *thread_write(void *arg)
{
int i, j;
for (j = 0; j < 3; j++) {
sleep(4);
pthread_rwlock_wrlock(&arealock);
for ( i = 0; i<ASIZE; i++) {
locked_area[i] = i + j*10;
}
printf("write thread finished.\n");
write_changed = 1;
pthread_rwlock_unlock(&arealock);
}
sleep(5);
finished = 1;
}

int main(int argc,char *argv[])
{
int res, ii;
pthread_t rthread[READ_THREAD_NUM], wthread;
int tno[READ_THREAD_NUM];

res = pthread_rwlock_init(&arealock,NULL);
if (res != 0) {
perror("arealock initialization failed!!\n");
exit(EXIT_FAILURE);
}

for (ii = 0; ii < READ_THREAD_NUM; ii++) {
tno[ii] = ii;
res = pthread_create(&rthread[ii], NULL, thread_read, &tno[ii]);
if (res != 0) {
perror("Thread creation failed!!\n");
exit(EXIT_FAILURE);
return -1;
}
}
res = pthread_create(&wthread, NULL, thread_write, NULL);
if (res != 0) {
perror("Thread creation failed\n");
exit(EXIT_FAILURE);
return -1;
}

for (ii = 0; ii < READ_THREAD_NUM; ii++) {
res = pthread_join(rthread[ii], NULL);
if (res != 0)
{
perror("Thread join failed\n");
exit(EXIT_FAILURE);
}
}
res = pthread_join(wthread, NULL);
if (res != 0) {
perror("Thread join failed\n");
exit(EXIT_FAILURE);
}
pthread_rwlock_destroy(&arealock);
exit(EXIT_SUCCESS);
}
-------------本文结束感谢您的阅读-------------
如果文章对您有帮助,也可以打赏支持喔!