前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >封装pthread_rwlock读写锁,实现嵌套调用

封装pthread_rwlock读写锁,实现嵌套调用

作者头像
10km
发布2019-05-25 20:36:00
1.6K0
发布2019-05-25 20:36:00
举报
文章被收录于专栏:10km的专栏10km的专栏10km的专栏

版权声明:本文为博主原创文章,转载请注明源地址。 https://cloud.tencent.com/developer/article/1433446

pthread_rwlock简介

pthread_rwlock 读写锁与pthread_mutex类似,但rwlock允许更高的并行性。mutex只有两种状态:加锁状态,不加锁状态,而且一次只有一个线程对其加锁。

rwlock可以有三种状态:读取模式加锁状态,写入模式加锁状态,不加锁状态。

在写入模式加锁时与mutex一样,是独占的,一次只有一个线程可以占有写模式的读写锁。

在读取模式加锁状态,允许多个线程可用同时共享读模式的读写锁。

rwlock嵌套调用

何为嵌套调用?

函数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 <errno.h>
#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 <pthread.h>

/**
 * @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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年10月08日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • pthread_rwlock简介
  • rwlock嵌套调用
  • 实现代码
  • 测试程序及调用示例
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档