LV05-03-线程同步-04-读写锁

本文主要是线程同步——读写锁的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
开发板 正点原子 i.MX6ULL Linux阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
STM32开发板 正点原子战舰V3(STM32F103ZET6)
点击查看本文参考资料
参考方向 参考原文
------
点击查看相关文件下载
--- ---

一、读写锁的概念

1. 基本概念与特性

互斥锁和自旋锁都是只有两个状态,即加锁和不加锁,而且一次只有一个线程可以对其加锁。

还有一种锁叫读写锁,读写锁从字面意思可以知道,它由读锁和写锁两部分构成,如果只读取共享资源用读锁加锁,如果要修改共享资源则用写锁加锁。读写锁也叫做共享互斥锁。当读写锁是读模式锁住时,就可以说成是共享模式锁住。当它是写模式锁住时,就可以说成是互斥模式锁住。

【说明】其实为了更好的区分,我个人更习惯把读写锁看成两把锁,一把读锁,一把写锁,后边的笔记也都这样写了,获取读锁就是读锁加锁,释放读锁就是读锁解锁;获取写锁就是写锁加锁,释放写锁就是写锁解锁。

读写锁有3种状态:读模式下的加锁状态、写模式下的加锁状态和不加锁状态。

image-20220526115359198

在应用程序当中,使用读写锁实现线程同步,当线程需要对共享数据进行读操作时,需要先获取读锁,当读取操作完成之后再释放读锁;当线程需要对共享数据进行写操作时,需要先获取到写锁,当写操作完成之后再释放写锁。

当读写锁处于写锁加锁状态时,在这个写锁被解锁之前,所有试图对这个写锁进行加锁操作(不管是以读模式加锁还是以写模式加锁)的线程都会被阻塞。
当读写锁处于读锁加锁状态时,所有试图以读模式对它进行加读锁的线程都可以加锁成功;但是任何以写模式对它进行加写锁的线程都会被阻塞,直到所有持有读模式锁的线程释放它们的读锁为止。

【注意事项】写锁是独占锁,因为任何时刻只能有一个线程持有写锁,类似互斥锁和自旋锁,而读锁是共享锁,因为读锁可以被多个线程同时持有

2. 读写哪个优先?

2.1 读优先锁

读优先锁希望的是,读锁能被更多的线程持有,以便提高读线程的并发性,它的工作方式是:当读线程 1 先持有了读锁,写线程 2 在获取写锁的时候,会被阻塞,并且在阻塞过程中,后续来的读线程 3 仍然可以成功获取读锁,最后直到读线程 13 释放读锁后,写线程 2 才可以成功获取写锁。如下图所示:

image-20220526135637537

2.2写优先锁

写优先锁是优先让写线程获取写锁,其工作方式是:当读线程 1 先持有了读锁,写线程 2 在获取写锁的时候,会被阻塞,并且在阻塞过程中,后续来的读线程 3 获取读锁时会失败,于是读线程 3 将被阻塞在获取读锁的操作,这样只要读线程1 释放读锁后,写线程 1 就可以成功获取读锁。

image-20220526140509441

2.3公平读写

读优先锁对于读线程并发性更好,但也不是没有问题。如果一直有读线程获取读锁,那么写线程将永远获取不到写锁,这就造成了写线程一直阻塞的情况。

写优先锁可以保证写线程优先获取写锁,但是如果一直有写线程获取写锁,读线程也会发生一直阻塞的情况。

既然不管优先读锁还是写锁,对方可能会出现一直阻塞的问题,那么就不偏袒任何一方,来一手公平读写,即用队列把获取锁的线程排队,不管是写线程还是读线程都按照先进先出的原则加锁,先来的自然就要优先获取锁,这样读线程仍然可以并发,也不会出现某一种锁一直阻塞的现象。

3. 应用场景

各操作系统对读写锁的实现各不相同,但当读写锁处于读锁加锁状态,而这时有一个线程试图以写模式获取锁时,该线程会被阻塞;而如果另一线程以读模式获取锁,则会成功获取到锁,对共享资源进行读操作。所以,读写锁非常适合于对共享数据读的次数远大于写的次数的情况。

二、基本操作

1. 读写锁初始化

读写锁与互斥锁的初始化类似,都是有两种方式,一种是动态方式,即通过pthread_rwlock_init()函数完成初始化;另一种是静态方式,通过宏进行初始化。

1.1 pthread_rwlock_init() 

1.1.1 函数说明

linux下可以使用man pthread_rwlock_init命令查看该函数的帮助手册。

1
2
3
4
5
6
/* Compile and link with -pthread. */
#include <pthread.h>
/* man 手册中的声明 */
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
/* 一些资料中的声明 */
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);

额……,😥函数声明又是这样,一些资料中使用的另一种声明,就还是使用自己容易理解的吧,反正传入的参数以及格式都是一样的。

【函数说明】该函数是以动态方式初始化一个读写锁。

【函数参数】

  • rwlockpthread_rwlock_t *类型,指向需要进行初始化操作的读写锁对象
  • attrconst pthread_rwlockattr_t *类型,指向一个pthread_rwlockattr_t 类型对象,该对象用于定义读写锁的属性,若将参数attr设置为NULL,则表示将读写锁的属性设置为默认值,在这种情况下其实就等价于PTHREAD_RWLOCK_INITIALIZER这种方式初始化,而不同之处在于,使用宏不进行错误检查。

【返回值】int类型,成功返回0;失败将返回一个非0的错误码。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_t rwlock; /* 定义全局变量 */
pthread_rwlock_init(&rwlock, NULL);/* 初始化读写锁 */
/* 或者 */
pthread_rwlock_t *rwlock; = malloc(sizeof(pthread_rwlock_t)); /* 定义为全局指针变量 */
pthread_rwlock_init(rwlock, NULL);/* 初始化读写锁 */

【注意事项】none

1.1.2 使用实例

暂无。

1.2 PTHREAD_RWLOCK_INITIALIZER 

1.2.1 函数说明

linux下可以使用man 3 pthread_rwlock_init命令查看该宏的定义格式。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; /* rwlock为读写锁名称,符合变量标识符定义规则即可。*/

【函数说明】该宏用于使用默认属性初始化一个读写锁。

【定义原型】该宏定义在pthread.h文件中,可以在终端中使用以下命令查看pthread.h文件位置:

1
locate pthread.h # 若出现locate 命令未安装之类的,按照提示安装即可

定义形式如下:

1
2
3
/* Read-write lock initializers.  */
# define PTHREAD_RWLOCK_INITIALIZER \
{ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }

【返回值】none

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

【注意事项】

(1)PTHREAD_RWLOCK_INITIALIZER宏已经携带了读写锁的默认属性。

(2)只适用于在定义的时候就直接进行初始化,对于其它情况则不能使用这种方式。

1.2.2 使用实例

暂无。

2. 加锁和解锁

2.1 pthread_mutex_lock() 

2.1.1 函数说明

linux下可以使用man pthread_rwlock_rdlock命令查看该函数的帮助手册。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

【函数说明】该函数以读模式对读写锁上锁,也就是获取读锁(获取不到时,会阻塞等待)。

【函数参数】

  • rwlockpthread_rwlock_t *类型,指向已经初始化过的读写锁对象。

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_t rwlock; /* 定义全局变量 */
pthread_rwlock_init(&rwlock, NULL);/* 初始化读写锁 */
pthread_rwlock_rdlock(&rwlock);/* 获取读锁 */

【注意事项】

(1)读写锁处于读模式加锁状态时,其它线程调用pthread_rwlock_rdlock()函数可以成功获取到锁,如果调用pthread_rwlock_wrlock()函数则不能获取到锁,从而陷入阻塞等待状态。

(2)当读写锁处于写模式加锁状态时,其它线程调用pthread_rwlock_rdlock()pthread_rwlock_wrlock()函数均会获取锁失败,从而陷入阻塞等待状态。

2.1.2 使用实例

暂无。

2.2 pthread_rwlock_tryrdlock() 

2.2.1 函数说明

linux下可以使用man pthread_rwlock_tryrdlock命令查看该函数的帮助手册。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

【函数说明】该函数以读模式对读写锁上锁,也就是获取读锁(获取不到时,不会阻塞等待,会直接返回)。

【函数参数】

  • mutexpthread_rwlock_t *类型,指向已经初始化过的读写锁对象。

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码;如果目标读写锁已经被其它线程锁住,则调用失败并返回,一般返回的错误码为EBUSY

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_tryrdlock(&rwlock);/* 具体看定义的方式,是变量还是指针变量,指针变量则不需要&符号。*/

【注意事项】在任何情况下,pthread_rwlock_tryrdlock()函数都不能被阻塞;它总是要么获得锁,要么失败并立即返回。

2.2.2 使用实例

暂无。

2.3 pthread_rwlock_wrlock() 

2.3.1 函数说明

linux下可以使用man pthread_rwlock_wrlock命令查看该函数的帮助手册。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

【函数说明】该函数以写模式对读写锁上锁,也就是获取写锁(获取不到时,会阻塞等待)。

【函数参数】

  • rwlockpthread_rwlock_t *类型,指向已经初始化过的读写锁对象。

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_t rwlock; /* 定义全局变量 */
pthread_rwlock_init(&rwlock, NULL);/* 初始化读写锁 */
pthread_rwlock_wrlock(&rwlock);/* 获取写锁 */

【注意事项】

(1)读写锁处于读模式加锁状态时,其它线程调用pthread_rwlock_rdlock()函数可以成功获取到锁,如果调用pthread_rwlock_wrlock()函数则不能获取到锁,从而陷入阻塞等待状态。

(2)当读写锁处于写模式加锁状态时,其它线程调用pthread_rwlock_rdlock()pthread_rwlock_wrlock()函数均会获取锁失败,从而陷入阻塞等待状态。

2.3.2 使用实例

暂无。

2.4 pthread_rwlock_trywrlock() 

2.4.1 函数说明

linux下可以使用man pthread_rwlock_trywrlock命令查看该函数的帮助手册。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

【函数说明】该函数以写模式对读写锁上锁,也就是获取写锁(获取不到时不会阻塞等待,会直接返回)。

【函数参数】

  • mutexpthread_rwlock_t *类型,指向已经初始化过的读写锁对象。

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码;如果目标读写锁已经被其它线程锁住,则调用失败并返回,一般返回的错误码为EBUSY

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_trywrlock(&rwlock);/* 具体看定义的方式,是变量还是指针变量,指针变量则不需要&符号。*/

【注意事项】在任何情况下,pthread_rwlock_trywrlock()函数都不能被阻塞;它总是要么获得锁,要么失败并立即返回。

2.4.2 使用实例

暂无。

2.5 pthread_rwlock_unlock() 

2.5.1 函数说明

linux下可以使用man pthread_rwlock_unlock命令查看该函数的帮助手册。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

【函数说明】该函数用于对读写锁解锁、释放读写锁(读锁或者写锁均可以由此函数释放)。

【函数参数】

  • mutexpthread_mutex_t *类型,指向已经初始化过的读写对象。

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_unlock(&rwlock);/* 具体看定义的方式,是变量还是指针变量,指针变量则不需要&符号。*/

【注意事项】none

2.5.2 使用实例

暂无。

3. 销毁读写锁

定义了读写锁之后,当不再需要读写锁时,应该将其销毁。

3.1 pthread_rwlock_destroy() 

3.1.1 函数说明

linux下可以使用man pthread_rwlock_destroy命令查看该函数的帮助手册。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

【函数说明】该函数用于销毁一个不再使用的读写锁。

【函数参数】

  • mutexpthread_rwlock_t *类型,指向已经初始化过的读写锁对象。

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_destroy(&rwlock);/* 具体看定义的方式,是变量还是指针变量,指针变量则不需要&符号。*/

【注意事项】none

3.1.2 使用实例

暂无。

4. 读写锁使用实例

前边终于把需要使用的函数学习完了,接下来就是一个实例啦。

点击查看实例
test.c
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <stdio.h>  /* fopen fputc*/
#include <pthread.h>/* pthread_create pthread_exit */
#include <unistd.h> /* sleep */
#include <string.h> /* strlen */

FILE *fp;/* 定义一个文件指针变量 */

#define macro_rwlock 1 /* 0,不使用读写锁;1,函数初始化读写锁;2,宏初始化读写锁 */
#if macro_rwlock == 1
pthread_rwlock_t rwlock;
#elif macro_rwlock == 2
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
#endif
void *threadRead(void *arg);
void *threadWrite1(void *arg);
void *threadWrite2(void *arg);

int main(int argc, char *argv[])
{
int ret;
pthread_t tid1, tid2, tid3, tid4;
/* 动态创建读写锁 */
#if macro_rwlock == 1
pthread_rwlock_init(&rwlock, NULL);
#endif
/* 1. 以追加读写的方式使用标准IO打开一个文件 */
fp = fopen("test.txt", "a+");
/* 2. 判断是否打开成功 */
if(fp == NULL)
{
perror("open error");
return -1;
}

/* 3. 创建四个线程 */
ret = pthread_create(&tid1, NULL, threadRead, (void *)1);
printf("This is threadRead thread create,ret=%d,tid1=%lu\n", ret, tid1);
ret = pthread_create(&tid2, NULL, threadRead, (void *)2);
printf("This is threadRead thread create,ret=%d,tid2=%lu\n", ret, tid2);

ret = pthread_create(&tid3, NULL, threadWrite1, NULL);
printf("This is threadWrite1 thread create,ret=%d,tid3=%lu\n", ret, tid3);
ret = pthread_create(&tid4, NULL, threadWrite2, NULL);
printf("This is threadWrite2 thread create,ret=%d,tid4=%lu\n", ret, tid4);

while(1)
{
sleep(1);
}
fclose(fp);
return 0;
}

void *threadRead(void *arg)
{
char buf[32]= {0};
/* 设置线程分离,结束后自动回收 */
pthread_detach(pthread_self());
printf("This is a threadRead test!\n");
/* 按行读取文件 */
while(1)
{
#if macro_rwlock == 1 || macro_rwlock == 2
pthread_rwlock_rdlock(&rwlock);
#endif
while(fgets(buf,32,fp)!=NULL)
{
printf("%d,rd=%s\n",(int)arg, buf);
usleep(1000);
}
#if macro_rwlock == 1 || macro_rwlock == 2
pthread_rwlock_unlock(&rwlock);
#endif
usleep(500*1000);
}


pthread_exit("threadRead exit!");
}

void *threadWrite1(void *arg)
{
int i = 0;
char str[]="I write threadWrite1 line\n";
/* 设置线程分离,结束后自动回收 */
pthread_detach(pthread_self());
printf("This is a threadWrite1 test!\n");
/* 按字符将字符串写入文件 */
while(1)
{
#if macro_rwlock == 1 || macro_rwlock == 2
pthread_rwlock_wrlock(&rwlock);
#endif
for(i = 0; i<strlen(str); i++)
{
fputc(str[i], fp);
usleep(100);/* us级休眠 */
}
#if macro_rwlock == 1 || macro_rwlock == 2
pthread_rwlock_unlock(&rwlock);
#endif
sleep(1);/* us级休眠 */
}

pthread_exit("threadWrite1 exit!");

}

void *threadWrite2(void *arg)
{
int i = 0;
char str[]="I write threadWrite2 line\n";
/* 设置线程分离,结束后自动回收 */
pthread_detach(pthread_self());
printf("This is a threadWrite2 test!\n");
/* 按字符将字符串写入文件 */
while(1)
{
#if macro_rwlock == 1 || macro_rwlock == 2
pthread_rwlock_wrlock(&rwlock);
#endif
for(i = 0; i<strlen(str); i++)
{
fputc(str[i], fp);
usleep(100);/* us级休眠 */
}
#if macro_rwlock == 1 || macro_rwlock == 2
pthread_rwlock_unlock(&rwlock);
#endif
sleep(1);/* us级休眠 */
}

pthread_exit("threadWrite2 exit!");

}

程序中的宏macro_rwlock用于决定是否初始化读写锁,若初始化的话,用什么方式初始化。

  • macro_rwlock0,则不使用读写锁。
  • macro_rwlock1,则使用函数pthread_rwlock_init()初始化读写锁。
  • macro_rwlock2,则使用宏PTHREAD_RWLOCK_INITIALIZER初始化读写锁。

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall -l pthread # 生成可执行文件 a.out 
./a.out # 执行可执行程序

然后,终端会有以下信息显示:

1
2
3
4
5
6
7
8
This is threadRead thread create,ret=0,tid1=139888351057472
This is a threadRead test!
This is threadRead thread create,ret=0,tid2=139888342664768
This is threadWrite1 thread create,ret=0,tid3=139888334272064
This is threadWrite2 thread create,ret=0,tid4=139888325879360
This is a threadWrite1 test!
This is a threadRead test!
This is a threadWrite2 test!

然后我们打开test.txt文件,查看文件内容如下:

1
2
3
4
5
II  wwrirtite eth rthreeaadWdWrirtiete12  lilinnee

I Iw rwirtie tet hrtheraedaWrdWirtie1te 2l ilnien
e
# 后边的省略 ......

该文件被四个线程共享,两个线程都是写入,未保护共享文件,导致文件写入出错。

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall -l pthread # 生成可执行文件 a.out 
./a.out # 执行可执行程序

然后,终端会有以下信息显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
This is threadRead thread create,ret=0,tid1=140066570966592
This is threadRead thread create,ret=0,tid2=140066562573888
This is a threadRead test!
This is threadWrite1 thread create,ret=0,tid3=140066554181184
This is a threadWrite2 test!
This is threadWrite2 thread create,ret=0,tid4=140066545788480
This is a threadWrite1 test!
This is a threadRead test!
1,rd=I write threadWrite1 line

2,rd=I write threadWrite2 line

1,rd=I write threadWrite1 line

然后我们打开test.txt文件,查看文件内容如下:

1
2
3
4
5
I write threadWrite1 line
I write threadWrite2 line
I write threadWrite1 line
I write threadWrite2 line
# 后边的省略 ......

该文件被两个线程共享,两个线程都是写入,使用读写锁保护共享文件,文件写入正常。但是呢,这里其实是有一个问题的,就是我们测试文件为空时,运行程序,程序写线程都会正常写入,但是读线程可能是不会运行的,即便是第二次再运行,读线程可能只会读一部分,然后停止读取,这是为什么呢?还记得前边介绍的读写哪个优先一节把,这就出现了写锁优先的情况,导致读线程一直被阻塞,写线程一直写入的情况,不过这里仅为测试读写锁,暂时不关心这个问题。

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall -l pthread # 生成可执行文件 a.out 
./a.out # 执行可执行程序

然后,终端会有以下信息显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
This is threadRead thread create,ret=0,tid1=139988447561280
This is a threadRead test!
This is threadRead thread create,ret=0,tid2=139988439168576
This is threadWrite1 thread create,ret=0,tid3=139988430775872
This is threadWrite2 thread create,ret=0,tid4=139988352104000
This is a threadWrite1 test!
1,rd=I write threadWrite1 line

This is a threadWrite2 test!
This is a threadRead test!
2,rd=I write threadWrite2 line

1,rd=I write threadWrite1 line

然后我们打开test.txt文件,查看文件内容如下:

1
2
3
4
5
I write threadWrite1 line
I write threadWrite2 line
I write threadWrite1 line
I write threadWrite1 line
# 后边的省略 ......

该文件被四个线程共享,两个线程都是写入,使用读写锁保护共享文件,文件写入正常。但是呢,这里其实是有一个问题的,就是我们测试文件为空时,运行程序,程序写线程都会正常写入,但是读线程可能是不会运行的,即便是第二次再运行,读线程可能只会读一部分,然后停止读取,这是为什么呢?还记得前边介绍的读写哪个优先一节把,这就出现了写锁优先的情况,导致读线程一直被阻塞,写线程一直写入的情况,不过这里仅为测试读写锁,暂时不关心这个问题。

三、读写锁属性

读写锁与互斥锁类似,也是有属性的。使用pthread_rwlock_init()函数初始化读写锁时可以设置互斥锁的属性,通过参数attr指定。如果将参数attr设置为NULL,则表示将互斥锁属性设置为默认值。如果不使用默认属性,在调用pthread_rwlock_init()函数时,参数attr必须要指向一个pthread_rwlockattr_t对象,而不能使用NULL

1. 属性对象初始化

1.1 pthread_rwlockattr_init() 

1.1.1 函数说明

linux下可以使用man pthread_rwlockattr_init命令查看该函数的帮助手册。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);

【函数说明】该函数初始化一个读写锁的属性对象(就是自定义创建的读写锁的各个默认属性)。

【函数参数】

  • attrpthread_rwlockattr_t *类型,指向需要初始化的读写锁属性对象

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_t rwlock; /* 定义读写锁(全局变量) */
pthread_rwlockattr_t attr; /* 定义读写锁属性(可以是局部变量) */
/* 初始化*/
pthread_rwlockattr_init(&attr); /* 初始化读写锁属性对象 */
pthread_rwlock_init(&rwlock, &attr);/* 初始化读写锁 */

【注意事项】none

1.1.2 使用实例

暂无。

2. 属性对象销毁

2.1 pthread_rwlockattr_destroy() 

2.1.1 函数说明

linux下可以使用man pthread_rwlockattr_destroy命令查看该函数的帮助手册。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);

【函数说明】该函数用于销毁一个不再使用的读写锁属性对象。

【函数参数】

  • attrpthread_rwlockattr_t *类型,指向已经初始化过且不需要再使用的读写锁属性对象

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
pthread_rwlock_t rwlock; /* 定义读写锁(全局变量) */
pthread_rwlockattr_t attr; /* 定义读写锁属性(可以是局部变量) */
/* 初始化*/
pthread_rwlockattr_init(&attr); /* 初始化读写锁属性对象 */
pthread_rwlock_init(&rwlock, &attr);/* 初始化读写锁 */
/* 销毁 */
pthread_rwlock_destroy(&rwlock); /* 销毁读写锁 */
pthread_rwlockattr_destroy(&attr); /* 销毁读写锁属性对象 */

【注意事项】none

2.1.2 使用实例

暂无。

3. 共享属性

3.1 pthread_rwlockattr_getpshared() 

3.1.1 函数说明

linux下可以使用man pthread_rwlockattr_getpshared命令查看该函数的帮助手册。

1
2
3
4
5
6
/* Compile and link with -pthread. */
#include <pthread.h>
/* man 手册函数声明 */
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr, int *restrict pshared);
/* 一些资料中的声明 */
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared);

额……,😥又是这样声明的一个函数,使用另一种声明吧,好理解一些。

【函数说明】该函数获取一个读写锁的共享属性。

【函数参数】

  • attrconst pthread_rwlockattr_t *类型,指向已经初始化过的读写锁属性对象
  • psharedint *类型,将获取的读写锁共享属性保存在参数pshared所指向的内存中。

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
int pshared;
pthread_rwlockattr_t attr;
/* 初始化*/
pthread_rwlockattr_init(&attr); /* 初始化读写锁属性对象 */
pthread_rwlockattr_getpshared(&attr, &pshared);/* 获取读写锁的共享属性 */

【注意事项】none

3.1.2 使用实例

暂无。

3.2 pthread_rwlockattr_setpshared() 

3.2.1 函数说明

linux下可以使用man pthread_rwlockattr_setpshared命令查看该函数的帮助手册。

1
2
3
/* Compile and link with -pthread. */
#include <pthread.h>
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);

【函数说明】该函数设置一个读写锁的共享属性。

【函数参数】

  • attrconst pthread_rwlockattr_t *类型,指向已经初始化过的读写锁属性对象
  • psharedint 类型,将读写锁共享属性设置为参数pshared所指向的内存中的值。
点击查看 pshared 可取的值
PTHREAD_PROCESS_SHARED共享读写锁。该读写锁可以在多个进程中的线程之间共享;
PTHREAD_PROCESS_PRIVATE私有读写锁。只有本进程内的线程才能够使用该读写锁,这是读写锁共享属性的默认值。

【返回值】int类型,用成功时返回0;失败将返回一个非0值的错误码。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <pthread.h>

/* 至少应该有的语句 */
int pshared;
pthread_rwlockattr_t attr;
/* 初始化*/
pthread_rwlockattr_init(&attr); /* 初始化读写锁属性对象 */
pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);/* 设置读写锁的共享属性 */

【注意事项】none

3.2.2 使用实例

暂无。

3.3共享属性设置实例

点击查看实例
test.c
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
#include <stdio.h>
#include <pthread.h>

pthread_rwlock_t rwlock; /* 定义读写锁(全局变量) */

int main(int argc, char *argv[])
{
int pshared;
pthread_rwlockattr_t attr; /* 定义读写锁属性(可以是局部变量) */
/* 初始化读写锁属性对象 */
pthread_rwlockattr_init(&attr);
/* 获取读写锁类型 */
pthread_rwlockattr_getpshared(&attr, &pshared);
printf("rwlock pshared:%d\n", pshared);
/* 设置读写锁类型 */
pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_rwlockattr_getpshared(&attr, &pshared);
printf("rwlock pshared:%d\n", pshared);
/* 初始化读写锁 */
pthread_rwlock_init(&rwlock, &attr);

/* 销毁读写锁属性对象和读写锁对象 */
pthread_rwlockattr_destroy(&attr);
pthread_rwlock_destroy(&rwlock);

return 0;
}

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall -lpthread # 生成可执行文件 a.out
./a.out # 执行可执行文件

然后,终端会有以下信息显示:

1
2
rwlock pshared:0
rwlock pshared:1