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; }