十、Linux下线程的读写锁

一、读写锁介绍

1. 读写锁的基本概念

读写锁是从互斥锁中发展下来的,读写锁将访问中的读操作和写操作区分开来对待,把对资源的共享者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。在某些读数据比改数据频繁的应用中,读写锁将会比互斥锁表现出很大的优越性。

2. 读写锁的状态

读写锁与互斥量类似。 但读写锁有更高的并行性,其特性为:写独占,读共享。

  • 读写锁有三种状态,读模式下加锁(共享)、写模式下加锁(独占)以及不加锁。
  • 一次只有一个线程可以占有写模式下的读写锁;但是多个线程可以同时占有读模式下的读写锁。
  • 读写锁在写加锁状态时,其他试图以写状态加锁的线程都会被阻塞。读写锁在读加锁状态时,如果有线程希望以写模式加锁时,必须阻塞,直到所有线程释放锁。
  • 当读写锁以读模式加锁时,如果有线程试图以写模式对其加锁,那么读写锁会阻塞随后的读模式锁请求,以避免读锁长期占用,而写锁得不到请求。
3. 读写锁特性
  • 读写锁是"写模式加锁"时, 解锁前,所有对该锁加锁的线程都会被阻塞。

  • 读写锁是"读模式加锁"时, 如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。

  • 读写锁是"读模式加锁"时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高

  • 读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。

  • 读写锁非常适合于对数据结构读的次数远大于写的情况。

二、读写锁的创建和销毁

#inlcude <pthread.h>
//读写锁初始化
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

//销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

/***使用方法***/
//读写锁的声明
pthread_rwlock_t  rwlock;
//读写锁的初始化
pthread_rwlock_init (&rwlock,NULL);
//读写锁的销毁
pthread_rwlock_destroy(pthread_rwlock_t *lock);
  • 函数参数:
    rwlock:读写锁
    attr:读写锁属性
  • 返回值:成功返回0,出错返回错误编号

三、读写锁的加锁和解锁

//读模式加锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
//写模式加锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
//解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
  • 函数参数:
    rwlock:读写锁
  • 返回值:
    成功返回 0;出错,返回错误编号
读写锁特性的案例验证
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
//定义读写锁
pthread_rwlock_t rwlock;
int main(int argc,char* argv[])
{
        //判断输入参数是否符合要求
        if(argc<3)
        {
                printf("usrage:%s [r|w|]\r\n",argv[0]);
                exit(1);
        }
        //初始化读写锁
        pthread_rwlock_init(&rwlock,NULL);
        //根据输入的参数第一次加读写锁
        if(!strcmp("r",argv[1])){
                //加读锁
                if(pthread_rwlock_rdlock(&rwlock)!=0){
                        printf("First read lock failure!\r\n");
                }
                else {
                        printf("First read lock success!\r\n");
                }
        }
        if(!strcmp("w",argv[1])){
                //加读锁
                if(pthread_rwlock_wrlock(&rwlock)!=0){
                        printf("First write lock failure!\r\n");
                }
                else {
                        printf("First read write success!\r\n");
                }
        }
      //根据输入的参数第二次加读写锁
        if(!strcmp("r",argv[2])){
                //加读锁
                if(pthread_rwlock_rdlock(&rwlock)!=0){
                        printf("First read lock failure!\r\n");
                }
                else {
                        printf("First read lock success!\r\n");
                }
        }
        if(!strcmp("w",argv[2])){
                //加读锁
                if(pthread_rwlock_wrlock(&rwlock)!=0){
                        printf("First write lock failure!\r\n");
                }
                else {
                        printf("First read write success!\r\n");
                }
        }
        //加几次锁,需要解几次锁
        pthread_rwlock_unlock(&rwlock);
        pthread_rwlock_unlock(&rwlock);
        //销毁创建的读写锁
        pthread_rwlock_destroy(&rwlock);

        return 0;

}

由运行结果可以知道,当都上读锁的时候,都可以上锁成功;先上读锁,再上写锁,写锁会阻塞;先上写锁后,不管是上读锁还是上写锁,都会失败。(红框中的提示可能与环境有关,暂时不管)
即验证了读写锁特性:1. 读和读:不排斥 2. 读和写:排斥 3. 写和写:排斥
在这里插入图片描述

四、案例改进

#ifndef __ATM_ACCOUNT_H__
#define __ATM_ACCOUNT_H__

#include <math.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

/** 账户信息 */
typedef struct {
    int         code;       ///< 银行账户的编码
    double      balance;    ///< 账户余额

    /** 建议互斥锁用来锁定一个账户(共享资源),和账户绑定再一起,
     *  尽量不设置成全局变量,否则可能出现一把锁去锁几百个账户,导致并发性能降低 */
    //pthread_mutex_t mutex;    ///< 定义一把互斥锁,用来对多线程操作的银行账户(共享资源)进行加锁

    pthread_rwlock_t     rwlock; ///<定义读写锁
}atm_Account;

/** 创建账户 */
extern atm_Account *atm_account_Create(int code, double balance);
/** 销毁账户 */
extern void atm_account_Destroy(atm_Account *account);
/** 取款 */
extern double atm_account_Withdraw(atm_Account *account, double amt);
/** 存款 */
extern double atm_account_Desposit(atm_Account *account, double amt);
/** 查看账户余额 */
extern double atm_account_BalanceGet(atm_Account *account);

#endif
#include "atm_account.h"

/** 创建账户 */
atm_Account *atm_account_Create(int code, double balance)
{
    atm_Account *account = (atm_Account *)malloc(sizeof(atm_Account));
    if(NULL == account) {
        return NULL;
    }

    account->code = code;
    account->balance = balance;

    /** 对互斥锁进行初始化 */
    //pthread_mutex_init(&account->mutex, NULL);

    /** 初始化读写锁 */
    pthread_rwlock_init(&account->rwlock, NULL);

    return account;
}

/** 销毁账户 */
void atm_account_Destroy(atm_Account *account)
{
    if(NULL == account){
        return ;
    }

    //pthread_mutex_destroy(&account->mutex);
    pthread_rwlock_destroy(&account->rwlock); ///< 销毁读写锁
    free(account);
}

/** 取款: 成功,则返回取款金额 */
double atm_account_Withdraw(atm_Account *account, double amt)
{
    if(NULL == account) {
        return 0.0;
    }

    /** 对共享资源(账户进行加锁) */
    //pthread_mutex_lock(&account->mutex);
    pthread_rwlock_wrlock(&account->rwlock); ///< 加写锁

    if(amt < 0 || amt > account->balance) {
        //pthread_mutex_unlock(&account->mutex);
        pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁
        return 0.0;
    }

    double balance_tmp = account->balance;
    sleep(1);
    balance_tmp -= amt;
    account->balance = balance_tmp;

    //pthread_mutex_unlock(&account->mutex);
    pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁
    return amt;
}

/** 存款: 返回存款的金额 */
double atm_account_Desposit(atm_Account *account, double amt)
{
    if(NULL == account){
        return 0.0;
    }

    /** 对共享资源(账户进行加锁) */
    //pthread_mutex_lock(&account->mutex);
    pthread_rwlock_wrlock(&account->rwlock); ///< 加写锁

    if(amt < 0){
        //pthread_mutex_unlock(&account->mutex);
        pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁
        return 0.0;
    }

    double balance_tmp = account->balance;
    sleep(1);
    balance_tmp += amt;
    account->balance = balance_tmp;

    //pthread_mutex_unlock(&account->mutex);
    pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁
    return amt;
}

/** 查看账户余额 */
double atm_account_BalanceGet(atm_Account *account)
{
    if(NULL == account){
        return 0.0;
    }

    /** 对共享资源(账户进行加锁) */
    //pthread_mutex_lock(&account->mutex);
    pthread_rwlock_rdlock(&account->rwlock); ///< 上读锁

    double balance_tmp = account->balance;
    //pthread_mutex_unlock(&account->mutex);
    pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁

    return balance_tmp;
}

从运行结果来看,使用读写锁和使用互斥锁运行的结果是一样的。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值