pthread_rwlock 读写锁与pthread_mutex类似,但rwlock允许更高的并行性。mutex只有两种状态:加锁状态,不加锁状态,而且一次只有一个线程对其加锁。
rwlock可以有三种状态:读取模式加锁状态,写入模式加锁状态,不加锁状态。
在写入模式加锁时与mutex一样,是独占的,一次只有一个线程可以占有写模式的读写锁。
在读取模式加锁状态,允许多个线程可用同时共享读模式的读写锁。
何为嵌套调用?
函数A中有申请/释放锁的调用,函数B也有申请/释放锁的调用。而且A在加锁状态会调用B,这样对于锁就来说就会在已经加锁的状态下再次申请加锁,这就是嵌套调用。
根据POSIX 的定义,rwlock的读取锁允许在同一个线程中嵌套调用,但写入锁不允许嵌套调用。并且写入锁与读取锁是互斥的,所以在写入锁状态下不可以再申请读取锁,反之在读取错状态下也不能再申请写入锁。
不允许读取模式下调用写入锁,这个可以理解,但pthread_rwlock
不允许嵌写入锁套调用,在实际应用中挺麻烦的。对于一个应用中可能有多个API申请rwlock
锁,这些API之间又互相有调用关系,这里如果能允许rwlock
嵌套调用,程序设计上会简单许多。
为了解决这个问题,我基于线程局部变量(thread local storage)对pthread_rwlock
又封装了一层,实现允许嵌套调用的nest_rwlock
。
nest_rwlock
不仅允许读取锁嵌套调用,也允许写入锁嵌套调用,还允许在写入锁状态嵌套调用读取锁。实现原理就是利用TLS变量记录当前线程的读取锁和写入锁的嵌套计数,根据嵌套计数决定是否执行加锁解锁(pthread_rwlock_wrlock
,pthread_rwlock_unlock
),对于写入锁状态下,本来就是独占模式,本就不需要申请读取锁,所以只要当线程知道自己的加锁状态,就可以判断是否执行加锁解锁。
实现nest_rwlock
所用的数据结构很简单,如下:
typedef struct _nest_rwlock_t{
int rd_nest; /** 读取锁嵌套计数 */
int wr_nest; /** 写入锁嵌套计数 */
pthread_rwlock_t* rwlock;
}nest_rwlock_t;
rd_nest
,wr_nest
分别记录当前线程下读取锁和写入锁的嵌套计数,每次加锁,对应的计数器会加1,解锁时计数器会减1.
对于写入锁,当执行加锁时,会判断,wr_nest
是否为0,为0时,代表是最外层的申请,这时会执行pthread_rwlock_wrlock
执行加锁,否则只会将wr_nest
加1,解锁时也一样,先将wr_nest
减1,然后判断wr_nest
是否为0,为0代表已经是嵌套调用的最外层,这时才执行pthread_rwlock_unlock
.
对于读取锁,因为pthread_rwlock本身就支持嵌套调用,所以每次调用加锁都会执行pthread_rwlock_rdlock
,同时计数器加1,解锁时也同样会执行pthread_rwlock_unlock
并将计数器减1.但rd_nest
并不是无用的,它代表了rwlock的加锁状态,当rd_nest
不为0时代表rwlock
在读取锁状态.
同样的道理当wr_nest
不为0时代表rwlock
在写入锁状态。
如果两个计数器都为0,代表rwlock
在不加锁状态。
如果两个计数器都不为0,那程序的逻辑肯定错了。
NOTE:
nest_rwlock_t对象在使用时必须定义为TLS变量(thread local storage)才能正确工作。参见后面的调用示例。
下面是实现代码,详见代码中的注释。
nest_rwlock.c
/* * nest_rwlock.c * * Created on: Oct 8, 2018 * Author: gyd */
#include
#include "nest_rwlock.h"
/** * @brief 对象初始化 * 本函数不负责对 pthread_rwlock_t 对象的初始化 * @param nest_rwlock * @param rwlock 已始化的 pthread_rwlock_t 对象 * @return 0 if success,otherwise error code */
int nest_rwlock_init(nest_rwlock_t*nest_rwlock,pthread_rwlock_t* rwlock)
{
int ret = 0;
if(NULL == nest_rwlock || NULL == rwlock)return EINVAL;
/** 不为NULL 代表已经初始化直接返回 */
if(nest_rwlock->rwlock) return 0;
nest_rwlock->rwlock = rwlock;
nest_rwlock->rd_nest = 0;
nest_rwlock->wr_nest = 0;
return ret;
}
/** * @brief 对象销毁 * 每个线程都要执行,本函数不负责对 pthread_rwlock_t 对象的销毁 * @param nest_rwlock * @return 0 if success,otherwise error code */
int nest_rwlock_destroy(nest_rwlock_t*nest_rwlock)
{
if(NULL == nest_rwlock)
return EINVAL;
nest_rwlock->rd_nest = 0;
nest_rwlock->wr_nest = 0;
nest_rwlock->rwlock = NULL;
return 0;
}
/** * 获取读取锁 * @param nest_rwlock * @return 返回 pthread_rwlock_rdlock 的返回值 */
int nest_rwlock_rdlock(nest_rwlock_t*nest_rwlock)
{
int ret;
if(NULL == nest_rwlock)
return EINVAL;
/** 写锁状态下为独占资源不需要再加读取锁,直接返回 */
if(nest_rwlock->wr_nest)
return 0;
/** 加锁成功计嵌套数器加1 */
if(0 == (ret = pthread_rwlock_rdlock(nest_rwlock->rwlock)))
++ nest_rwlock->rd_nest;
return ret;
}
/** * 释放读取锁 * @param nest_rwlock * @return 返回 pthread_rwlock_unlock 的返回值 */
int nest_rwlock_rdunlock(nest_rwlock_t*nest_rwlock)
{
int ret;
if(NULL == nest_rwlock)
return EINVAL;
/** 写锁状态下没有加读取锁,直接返回 */
if(nest_rwlock->wr_nest)
return 0;
/** 未加锁状态报错 */
if(0 == nest_rwlock->rd_nest)
return EPERM;
/** 解锁成功计嵌套数器减1 */
if(0 == (ret = pthread_rwlock_unlock(nest_rwlock->rwlock)))
-- nest_rwlock->rd_nest;
return ret;
}
/** * 获取写入锁 * @param nest_rwlock * @return 返回 pthread_rwlock_wrlock 的返回值 */
int nest_rwlock_wrlock(nest_rwlock_t*nest_rwlock)
{
int ret = 0;
if(NULL == nest_rwlock)
return EINVAL;
/** 已经加读取锁时报错 */
if(nest_rwlock->rd_nest)
return EDEADLK;
/** 写入锁嵌套计数为0时执行解锁,否则直接将嵌套计数器减1 */
if(0 == nest_rwlock->wr_nest)
ret = pthread_rwlock_wrlock(nest_rwlock->rwlock);
if(0 == ret)
++ nest_rwlock->wr_nest;
return ret;
}
/** * 释放写入锁 * @param nest_rwlock * @return 返回 pthread_rwlock_unlock 的返回值 */
int nest_rwlock_wrunlock(nest_rwlock_t*nest_rwlock)
{
int ret = 0;
if(NULL == nest_rwlock)
return EINVAL;
/** 已经加读取锁时报错 */
if(nest_rwlock->rd_nest)
return EDEADLK;
/** 未加锁状态报错 */
if(0 == nest_rwlock->wr_nest)
return EPERM;
-- nest_rwlock->wr_nest;
/** 写入嵌套计数器为0时执行解锁 */
if(0 == nest_rwlock->wr_nest)
ret = pthread_rwlock_unlock(nest_rwlock->rwlock);
return ret;
}
/** * 自动判断读写锁状态执行对应的解锁函数(pthread_rwlock_unlock,pthread_rwlock_unlock) * @param nest_rwlock * @return */
int nest_rwlock_unlock(nest_rwlock_t*nest_rwlock)
{
if(NULL == nest_rwlock)
return EINVAL;
/** 已经加读取锁时执行读取锁解锁 */
if(nest_rwlock->rd_nest && 0 == nest_rwlock->wr_nest)
return nest_rwlock_rdunlock(nest_rwlock);
/** 已经加写入锁时执行写入锁解锁 */
if(nest_rwlock->wr_nest && 0 == nest_rwlock->rd_nest)
return nest_rwlock_wrunlock(nest_rwlock);
return EPERM;
}
nest_rwlock.h
/* * nest_rwlock.h * * Created on: Oct 8, 2018 * Author: gyd */
#ifndef SRC_CORE_THREADSAFE_NEST_RWLOCK_H_
#define SRC_CORE_THREADSAFE_NEST_RWLOCK_H_
#include
/** * @brief 基于 pthread_rwlock_t 实现读写锁嵌套调用封装 * 读写锁都可以嵌套调用, * 写入锁状态下可以嵌套调用读取锁,读取锁状态下不可嵌套调用写入锁 * 必须定义为TLS变量(thread local storage) */
typedef struct _nest_rwlock_t{
int rd_nest; /** 读取锁嵌套计数 */
int wr_nest; /** 写入锁嵌套计数 */
pthread_rwlock_t* rwlock;
}nest_rwlock_t;
#ifdef __cplusplus
extern "C" {
#endif
int nest_rwlock_init(nest_rwlock_t*nest_rwlock,pthread_rwlock_t* rwlock);
int nest_rwlock_destroy(nest_rwlock_t*nest_rwlock);
int nest_rwlock_rdlock(nest_rwlock_t*nest_rwlock);
int nest_rwlock_rdunlock(nest_rwlock_t*nest_rwlock);
int nest_rwlock_wrlock(nest_rwlock_t*nest_rwlock);
int nest_rwlock_wrunlock(nest_rwlock_t*nest_rwlock);
int nest_rwlock_unlock(nest_rwlock_t*nest_rwlock);
#ifdef __cplusplus
}
#endif
#endif /* SRC_CORE_THREADSAFE_NEST_RWLOCK_H_ */
测试程序及调用示例, linux gcc,windows MSVC/MinGW测试通过,windows 下MSVC编译需要POSIX Threads (pthreads) for Win32支持。
test_rwlock.c
/* * test_rwlock.c * * Created on: Oct 8, 2018 * Author: gyd */
#include
#include
#include
#include "nest_rwlock.h"
#if _MSC_VER
#define __TLS __declspec(thread)
#else
#define __TLS __thread
#endif
#define MAXDATA 1024
#define MAXREDER 100
#define MAXWRITER 100
struct
{
pthread_rwlock_t rwlock;
char datas[MAXDATA];
} shared;
/** TLS变量 */
static __TLS nest_rwlock_t tls_lock;
static inline int is_empty_line(const char*str)
{
return 0 == strcmp(str,"\n") || 0 == strcmp(str,"\r\n");
}
/** 返回当前线程id */
static inline unsigned int pthread_id()
{
#ifdef PTW32_VERSION
/** 使用 prthread for win32 版本的pthread*/
return pthread_getw32threadid_np(pthread_self());
#else
return (unsigned int)pthread_self();
#endif
}
/** 统计shared.datas中的单词数量,用于嵌套测试 */
static int world_count()
{
nest_rwlock_init(&tls_lock,&shared.rwlock);
nest_rwlock_rdlock(&tls_lock);
const char* str = shared.datas;
const char* sep = " \n,!;?:+";
unsigned int cnt = 0;
do {
str = strpbrk(str, sep); // find separator
if(str) str += strspn(str, sep); // skip separator
++cnt; // increment word count
} while(str && *str);
nest_rwlock_unlock(&tls_lock);
return cnt;
}
static void *reader(void *arg)
{
int emp;
printf("Reader beginning read message.thread 0x%x\n",pthread_id());
/** 初始化tls变量 */
nest_rwlock_init(&tls_lock,&shared.rwlock);
const char* str = shared.datas;
do
{
nest_rwlock_rdlock(&tls_lock);
if(!(emp = is_empty_line(str)) && strlen(str))
{
printf("thread 0x%x Read message is: %s\n",pthread_id(),str);
str += strlen(str);
/** 嵌套调用测试 */
printf("total %u words in shared buffer\n", world_count());
}
nest_rwlock_unlock(&tls_lock);
/** 输入空行结束循环 */
}while(!emp);
printf("Reader stope.thread 0x%x\n",pthread_id());
return NULL;
}
static void *writer(void *arg)
{
char datas[MAXDATA];
printf("Writers beginning write message.thread 0x%x\n",pthread_id());
/** 初始化tls变量 */
nest_rwlock_init(&tls_lock,&shared.rwlock);
do
{
printf("Enter the write message: thread:0x%x\n",pthread_id());
fgets(datas,sizeof(datas),stdin);
nest_rwlock_wrlock(&tls_lock);
strcat(shared.datas,datas);
/** 嵌套调用测试 */
printf("total %u words in shared buffer\n", world_count());
nest_rwlock_unlock(&tls_lock);
/** 输入空行结束循环 */
}while(! is_empty_line(datas));
printf("Writers stop.thread 0x%x\n",pthread_id());
return NULL;
}
int main(int argc,char *argv[])
{
int i,reader_count,writer_count;
pthread_t tid_reader[MAXREDER],tid_writer[MAXWRITER];
if(argc != 3)
{
printf("usage : test_rwlock ${readercount} ${writercount}\n"
" Enter empty line to finish program \n");
exit(0);
}
reader_count = atoi(argv[1]);
writer_count = atoi(argv[2]);
printf("reader_count=%d,writer_count=%d\n",reader_count,writer_count);
pthread_rwlock_init(&shared.rwlock,NULL);
/** 启动写线程 */
for(i=0; i<writer_count; ++i)
pthread_create(&tid_writer[i],NULL,writer,NULL);
/** 启动读线程 */
for(i=0; i<reader_count; ++i)
pthread_create(&tid_reader[i],NULL,reader,NULL);
for(i=0; i<writer_count; ++i)
pthread_join(tid_writer[i],NULL);
for(i=0; i<reader_count; ++i)
pthread_join(tid_reader[i],NULL);
pthread_rwlock_destroy(&shared.rwlock);
exit(0);
}
http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_rwlock_rdlock.html
http://pubs.opengroup.org/onlinepubs/009696899/basedefs/pthread.h.html
https://www.cnblogs.com/diegodu/p/3890450.html