第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]\n", 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\n");
        }else{
            printf("firest read lock success\n");
        }
    }else{
        //加写锁
        if(pthread_rwlock_wrlock(&rwlock) != 0){
            printf("first write lock failure\n");
        }else{
            printf("firest write lock success\n");
        }
    }
    //第2次加锁
    if(!strcmp("r", argv[2])){
        //加读锁
        if(pthread_rwlock_rdlock(&rwlock) != 0){
            printf("second read lock failure\n");
        }else{
            printf("second read lock success\n");
        }
    }else{
        //加写锁
        if(pthread_rwlock_wrlock(&rwlock) != 0){
            printf("second write lock failure\n");
        }else{
            printf("second write lock success\n");
        }
    }

    //释放锁
    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)\n", 
              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)\n", 
              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\n", get_balance(a));

    destroy_account(a);

    return 0;
}

原文链接: https://www.cnblogs.com/5iedu/p/6415541.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    第9章 线程编程(5)_线程同步2:读写锁

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/393605

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月3日 下午3:16
下一篇 2023年4月3日 下午3:16

相关推荐