Skip to content

LV020-读写锁

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

一、读写锁的概念

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 命令查看该函数的帮助手册。

c
/* 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 的错误码。

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

c
/* 需要包含的头文件 */
#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 命令查看该宏的定义格式。

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

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

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

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

定义形式如下:

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

返回值none

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

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

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

函数参数

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

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

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

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

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

函数参数

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

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

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

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

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

函数参数

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

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

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

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

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

函数参数

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

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

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

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

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

函数参数

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

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

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

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

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

函数参数

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

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

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

c
/* 需要包含的头文件 */
#include <pthread.h>

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

注意事项none

3.1.2 使用实例

暂无。

4. 读写锁使用实例

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

4.1 测试源码

c
#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 初始化读写锁。

4.2 macro_mutex == 0

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

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

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

shell
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 文件,查看文件内容如下:

shell
II  wwrirtite eth rthreeaadWdWrirtiete12  lilinnee

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

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

4.3 macro_mutex == 1

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

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

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

shell
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 文件,查看文件内容如下:

shell
I write threadWrite1 line
I write threadWrite2 line
I write threadWrite1 line
I write threadWrite2 line
# 后边的省略 ......

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

4.4 macro_mutex == 2

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

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

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

shell
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 文件,查看文件内容如下:

shell
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 命令查看该函数的帮助手册。

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

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

函数参数

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

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

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

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

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

函数参数

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

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

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

c
/* 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 值的错误码。

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

c
/* 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` 值的错误码。

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

c
/* 需要包含的头文件 */
#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 共享属性设置实例

c
#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;
}

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

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

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

shell
rwlock pshared:0
rwlock pshared:1