• Linux多线程C++版(七) 线程互斥方式-----读写锁


    1.读写锁基本概念
    • 线程使用互斥锁缺乏读并发性
    • 当读操作比较多,写操作比较少时,可使用读写锁提高线程读并发性
    • 读写锁数据类型 pthread_rwlock_t
    2.读写锁创建和销毁
    //定义读写锁
    pthread_rwlock_t  rwlock;
    //读写锁的初始化
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
    //读写锁的销毁
    int pthread_rwlock_destroy(pthread_rwlock_t *restrict rwlock);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 参数
      • rwlock:读写锁
      • attr:读写锁的属性
    3.读写锁的加锁和解锁
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); //功能:加读锁
    int pthread_rwlock_wdlock(pthread_rwlock_t *rwlock); //功能:加写锁
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); //功能:释放锁
    返回:成功返回0,出错返回错误编号
    
    • 1
    • 2
    • 3
    • 4
    • 参数
      • rwlock:读写锁
    4.代码了解读写锁
    #include 
    #include 
    #include 
    #include 
    //定义读写锁
    pthread_rwlock_t rwlock;
    
    int main(int argc,char *argv[]){
        if(argc<3){
            printf("-usage:%s [r|w] [r|w]/n",argv[0]);//"-usage代表你的程序
            exit(1);
        }
        //读写锁初始化
        pthread_rwlock_init(&rwlock,null);
        
        /*
        	strcmp函数用于比较两个字符串并根据比较结果返回整数。
        	基本形式为strcmp(str1,str2),
        	若str1=str2,则返回零;
        	若str1str2,则返回正数。
        */
        if(!strcmp("r",argv[1])){
            //加读锁
            if(pthread_rwlock_rdlock(&rwlock) !=0){
                printf("first read lock failure\n");
            }else{
                printf("first read lock sucess\n");
            }
        }else if(!strcmp("w",argv[1])){
            //加写锁
            if(pthread_rwlock_wdlock(&rwlock) !=0){
                printf("first write lock failure\n");
            }else{
                printf("first write lock sucess\n");
            }
        }
        
        if(!strcmp("r",argv[2])){
            //加读锁
            if(pthread_rwlock_rdlock(&rwlock) !=0){
                printf("second read lock failure\n");
            }else{
                printf("second read lock sucess\n");
            }
        }else if(!strcmp("w",argv[2])){
            //加写锁
            if(pthread_rwlock_wdlock(&rwlock) !=0){
                printf("second write lock failure\n");
            }else{
                printf("second write lock sucess\n");
            }
        }
        
        //释放读写锁
        pthread_rwlock_unlock(&rwlock);
        pthread_rwlock_unlock(&rwlock);
        return 0;
    }
    
    • 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

    程序代码输出:

    ​ 都是读锁,是都成功的。

    ​ 先读后写,读锁成功加上,加写锁堵塞,要等读锁释放

    ​ 先写后读,写锁成功加上,加读锁失败

    ​ 都是写锁,第一个写锁加上,第二次写锁失败

    总结特性:读和读互不影响,读和写相互排斥,写和写相互排斥

    在这里插入图片描述

    5.线程互斥案例—ATM取钱–使用读写锁

    头文件 account.h

    #ifndef __ACCOUNT_H__
    #define __ACCOUNT_H__
    #include 
    //银行账户结构体
    typedef struct{
        int code;//卡号
        double balance;//余额
        //定义一把互斥锁,用来对多线程操作
        //银行账户(共享资源)进行加锁保护
        /*
        	建议互斥锁和账户(共享资源)绑定一起,用来锁定一个账户(共享资源)
        	尽量不设置全局变量,否则肯能出现一把锁去锁几百个账户,导致并发性能降低
        */
        //pthread_mutex_t mutex; 定义互斥锁
        //定义读写锁
        pthread_rwlock_t  rwlock;
        
    }Account;
    //创建账户
    extern Account* creat_account(int code,double balance);
    //销毁账户
    extern void destory_account(Account *a);
    //取款
    extern double withdraw(Account *a,double amt);
    //存款
    extern double deposit(Account *a,double amt);
    //查看账户余额
    extern double get_balance(Account *a);
    #endif
    
    • 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

    功能文件account.c

    #include "account.h"
    #include 
    #include 
    #include 
    
    //创建账户
    Account* creat_account(int code,double balance){
        //创建账户在堆当中
        Account *a = (Account*)malloc(sizeof(Account));
        assert(a != NULL);//用来判断a指针所需要开辟的空间,开辟成功了吗,成功就继续,否则就程序终止运行。
        a->code = code;
        a->balance = balance;
        //对读写锁进行初始化
        pthread_rwlock_init(&a->rwlock,null);
        return a;
    }
    //销毁账户
    void destory_account(Account *a){
        assert(a != NULL);
        //销毁读写锁
        pthread_rwlock_destroy(&a->rwlock)
        free(a);//释放空间
    }
    //取款
    double withdraw(Account *a,double amt){
        assert(a != NULL);//判断指针a是否为空
        /*
        	从成功上锁,到释放锁之间对共享资源操作的区间称为临界区
        */
        //加写锁
        pthread_rwlock_wrlock(&a->rwlock);
            
        if(amt < 0 || amt > a->balance){
            //释放读写锁
            pthread_rwlock_unlock(&a->rwlock);
            return 0.0;
        }
        double balance = a->balance;
        sleep(1);
        balance -= amt;
        a->balance = balance;
        //释放读写锁
        pthread_rwlock_unlock(&a->rwlock);
        return amt;
    }
    //存款
    double deposit(Account *a,double amt){
       assert(a != NULL);
       //加写锁
       pthread_rwlock_wrlock(&a->rwlock);//对共享共享资源(账户)加锁
       if(amt < 0){
           //释放读写锁
           pthread_rwlock_unlock(&a->rwlock);
           return 0.0;
       }
        double balance = a->balance;
        sleep(1);
        balance += amt;
        a->balance = balance;
        //释放读写锁
        pthread_rwlock_unlock(&a->rwlock);
        return amt;
    }
    //查看账户余额
    double get_balance(Account *a){
        assert(a != NULL);
        //加读锁
        pthread_rwlock_rdlock(&a->rwlock);//对共享共享资源(账户)加锁
        double balance = a->balance;
        //释放读写锁
        pthread_rwlock_unlock(&a->rwlock);
        return balanec;
    }
    
    • 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

    执行文件 account_test.c

    #include "account.h"
    #include 
    #include 
    #include 
    #include 
    
    //操作者的结构体
    typedef struct{
        char  name[20];//操作者姓名
        Account *account;//账户
        double amt;//金额
    }OperArg
    //定义取款操作的线程运行函数
    void* withdraw_fn(void *arg){
        OperArg *oa = (OperArg*)arg;
        //deposit存款函数
        double amt = deposit(oa->account,oa->amt);
        printf("%8s(0x%lx) deposit %f from account %d/n",oa->name,pthread_self(),amt,oa->account->code);
        return (void*)0;
    }
    //定义存款操作的线程运行函数
    void* deposit_fn(void *arg){
        OperArg *oa = (OperArg*)arg;
        //withdraw取款函数
        double amt = withdraw(oa->account,oa->amt);
        printf("%8s(0x%lx) withdraw %f from account %d/n",oa->name,pthread_self(),amt,oa->account->code);
        return (void*)0;
    }
    //定义查看银行账户的线程运行函数
    void* check_fn(void *arg){
        
    }
    int main(void){
        int err;
        pthread_t boy,girl;
        Account *a = create_account(10001,10000)//卡号,余额
            
        OperArg o1 , o2;//两个操作者
        strcpy(o1.name,"boy");//strcpy()把第二个参数值,放到第一个参数中
        o1.account = a;
        o1.amt = 10000;
        
        strcpy(o2.name,"girl");
        o2.account = a;
        o2.amt = 10000;
        
        //启动两个子线程(boy和girl)同时去操作同一个银行账户
        if((err = pthread_create(&boy,null,withdraw_fn,(void*)&o1)!=0){
            printf("pthread create error");
        }
        if((err = pthread_create(&girl,null,withdraw_fn,(void*)&o2)!=0){
            printf("pthread create error");
        }
        
        pthread_join(boy,null);
        pthread_join(girl,null);
           
        //查看账户余额
        printf("account balance %f/n",get_balance(a));
        //销毁账户
        destroy_account(a);
        return 0;
    }
    
    • 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

    程序运行结果:

    在这里插入图片描述

  • 相关阅读:
    词嵌入数据集的预处理--Word2Vec实现(一)
    RHCE 9.0培训课程之容器技术的运行
    【神印王座】陈樱儿假扮魔神皇,皓晨想杀人灭口,采儿施展禁制,月夜成功自保
    选择工业交换机时,需要关注哪些方面的性能?
    RabbitMQ 基本概念、docker安装
    DA14531在三星手机手写笔的应用让我打开眼镜
    Spring面试题22:Spring支持哪些ORM框架?优缺点分别是什么?Spring可以通过哪些方式访问Hibernate?
    MyBatis的自定义插件
    学习C++语言可以适用于哪些方面
    跟误告警说再见,Smart Metrics 帮你用算法配告警
  • 原文地址:https://blog.csdn.net/kaszxc/article/details/128121706