zoukankan      html  css  js  c++  java
  • 第9章 线程编程(5)_线程同步2:读写锁

    5.3 读写锁

    (1)读写锁简介

      ①线程使用互斥锁缺乏读并发性

      ②当读操作较多,写操作较少时,可使用读写锁提高线程并发性

      ③读写锁数据类型:pthread_rwlock_t

    (2)读写锁的操作

      ①创建和释放读写锁

    头文件

    #include <pthread.h>

    函数

    int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr); //初始化

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

    返回值

    成功返回0,否则返回错误编号

    参数

    rwlock:读写锁

    attr:锁的属性

      ②加锁和解锁

    头文件

    #include <pthread.h>

    函数

    int pthread_rdlock(pthread_rwlock_t* rwlock); //加读锁

    int pthread_rwlock(pthread_rwlock_t* rwlock); //加写锁

    int pthread_unlock(pthread_rwlock_t* rwlock);  //释放锁

    返回值

    成功返回0,否则返回错误编号

    参数

    rwlock:读写锁

      ③读写锁的进程共享属性读写锁支持的唯一属性

    头文件

    #include <pthread.h>

    函数

    int pthread_rwlockattr_getpshared(const pthread_rwlockxattr_t* attr, int* pshared);//获取读写锁的共享属性,结果存在入pshared中

    int pthread_rwlockattr_setpshared(pthread_rwlockattr_t* attr, int pshared); //设置读写锁的进程共享属性

    返回值

    成功返回0,否则返回错误编号

    参数

    (1)attr:读写锁的属性

    (2)pshared:进程共享属性:

      ①PTHREAD_PROCESS_PRIVATE(默认情况):锁只能用于一个进程内部的两个线程进行互斥

      ②PTHREAD_PROCESS_SHARED:可用于两个不同进程中的线程进行互斥

    【编程实验】读写锁的特点

    (1)两次都上读锁,则成功。即读、读锁是不排斥的。

    (2)先上读、再上写锁时,第1次成功,后一次阻塞。即排斥的。

    (3)先上写锁,再上读锁,后一次会失败,即排斥的。

    (4)两次都上写锁,后一次会失败,即排斥的。

    //rwlock_feature.c

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /*对读写锁多次上锁操作*/
    
    //定义读写锁
    pthread_rwlock_t rwlock;
    
    int main(int argc, char* argv[])
    {
        if(argc < 3){
            printf("-usage: %s [r|w] [r|w]
    ", argv[0]);
            exit(1);
        }
    
        //读写锁初始化
        pthread_rwlock_init(&rwlock, NULL);
    
        //第1次加锁
        if(!strcmp("r", argv[1])){
            //加读锁
            if(pthread_rwlock_rdlock(&rwlock) != 0){
                printf("first read lock failure
    ");
            }else{
                printf("firest read lock success
    ");
            }
        }else{
            //加写锁
            if(pthread_rwlock_wrlock(&rwlock) != 0){
                printf("first write lock failure
    ");
            }else{
                printf("firest write lock success
    ");
            }
        }
        //第2次加锁
        if(!strcmp("r", argv[2])){
            //加读锁
            if(pthread_rwlock_rdlock(&rwlock) != 0){
                printf("second read lock failure
    ");
            }else{
                printf("second read lock success
    ");
            }
        }else{
            //加写锁
            if(pthread_rwlock_wrlock(&rwlock) != 0){
                printf("second write lock failure
    ");
            }else{
                printf("second write lock success
    ");
            }
        }
    
        //释放锁
        pthread_rwlock_unlock(&rwlock);
        pthread_rwlock_unlock(&rwlock);
    
        //销毁锁
        pthread_rwlock_destroy(&rwlock);
    
        return 0;
    }
    /*输出结果:
     [root@bogon]# bin/rwlock_feature r r //读、读 ==> 成功
     firest read lock success
     second read lock success
     [root@bogon]# bin/rwlock_feature r w //读、写 ==> 1成功,2阻塞
     firest read lock success
     ^C
     [root@bogon]# bin/rwlock_feature w r //写、读 ==> 1成功,2失败
     firest write lock success
     second read lock failure
     [root@bogon]# bin/rwlock_feature w w //写、写 ==> 1成功,2失败
     firest write lock success
     second write lock failure
     [root@bogon]# 
     */

    【编程实验】银行帐号(ATM)(利用读写锁提高查询的并发性)

    //account.h

    #ifndef __ACCOUNT_H__
    #define __ACCOUNT_H__
    #include <pthread.h>
    
    typedef struct
    {
        int      code;    //帐号
        double   balance; //余额
    
        //使用读写锁,用来对多线程操作的银行帐户(共享资源)进行加锁保护。
        /*
         *建议读写锁和一个帐户绑定。尽量不设置成全局变量,否则可能出更一
         *锁去锁定多个帐户,导致并发性能降低。
         */
        pthread_rwlock_t rwlock;
    }Account;
    
    //创建账户
    extern Account* create_account(int code, double balance);
    //销毁帐户
    extern void destroy_account(Account* a);
    //取款
    extern double withdraw(Account* a, double amt); //amt == amount
    //存款
    extern double deposit(Account* a, double amt);
    //查看帐户余额
    extern double get_balance(Account* a);
    
    #endif  //__ACCOUNT_H__

    //account.c

    #include "account.h"
    #include <malloc.h>
    #include <string.h>
    #include <assert.h>
    
    //创建账户
    Account* create_account(int code, double balance)
    {
        Account* ret = (Account*)malloc(sizeof(Account));
        assert(ret != NULL);
    
        ret->code = code;
        ret->balance = balance;
        
        //对读写锁进行初始化
        pthread_rwlock_init(&ret->rwlock, NULL);
     
        return ret;
    }
    
    //销毁帐户
    void destroy_account(Account* a)
    {
        assert( a != NULL);
        
        //销毁读写锁
        pthread_rwlock_destroy(&a->rwlock);
    
        free(a);
    }
    
    //取款
    double withdraw(Account* a, double amt) //amt == amount
    {
        assert(a != NULL);
    
        //加写锁
        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);
    
        if(amt < 0){
            return 0.0;
        }
        
        pthread_rwlock_wrlock(&a->rwlock);
    
        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 balance;
    }

    //account_test.c

    #include "account.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    //#include <string.h> //for strcpy
    
    typedef struct
    {
        char name[20];
        Account* account;
        double amt;
    }OperArg;
    
    //定义取款操作的线程函数
    void* withdraw_fn(void* arg)
    {
        OperArg* oa = (OperArg*)arg;
       
        double amt = withdraw(oa->account, oa->amt);
        printf("%s(0x%lx) withdraw %f from account(%d)
    ", 
                  oa->name,pthread_self(), amt, oa->account->code);
    
        return (void*)0;
    }
    
    //定义存款操作的线程函数
    void* deposit_fn(void* arg)
    {
        OperArg* oa = (OperArg*)arg;
        
        double amt = deposit(oa->account, oa->amt);
        printf("%s(0x%lx) deposit %f from account(%d)
    ", 
                  oa->name,pthread_self(), amt, oa->account->code);
    
        return (void*)0;
    }
    
    int main(void)
    {
        int err = 0;
        pthread_t boy, girl;
    
        Account* a = create_account(100001, 10000);
        OperArg o1 = {"boy",  a, 10000}; //strcpy(o1.name, "boy");
        OperArg o2 = {"girl", a, 10000};
        
        //启动两个子线程(boy和girl线程)同时去操作同一个银行帐户
        if((err = pthread_create(&boy, NULL, withdraw_fn, (void*)&o1)) != 0){
            perror("pthread_create error");
        }
    
        if((err = pthread_create(&girl, NULL, withdraw_fn, (void*)&o2)) != 0){
            perror("pthread_create error");
        }
        
        pthread_join(boy, NULL);
        pthread_join(girl, NULL);
    
        //查看余额
        printf("account balance: %f
    ", get_balance(a));
        
        destroy_account(a);
        
        return 0;
    }
  • 相关阅读:
    QSslError 类
    QNetworkRequest 请求类
    QFTP走了以后QNetworkAccessManager出现了
    Android之SQLite总结
    Android之Handler机制
    Android之SeekBar总结(一)
    Android之测试相关知识点
    Android数据储存之SharedPreferences总结
    android studio的常用快捷键
    BitmapFactory.Options详解
  • 原文地址:https://www.cnblogs.com/5iedu/p/6415541.html
Copyright © 2011-2022 走看看