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

浙公网安备 33010602011771号