Linux多线程C++版(五) 线程同步和线程互斥
创始人
2024-02-17 04:24:23

目录

        • 1.线程同步和线程互斥
        • 2.线程互斥案例---ATM取钱--没有使用互斥锁
        • 3.线程互斥----互斥锁(互斥量)
        • 4.互斥锁创建和销毁
        • 5.互斥锁上锁和解锁
        • 6.线程互斥案例---ATM取钱--使用互斥锁
        • 7.互斥锁属性创建和销毁
        • 8.互斥锁属性之一---进程共享属性操作
        • 9.互斥锁属性之一----互斥锁类型操作
        • 10.代码了解互斥锁属性

1.线程同步和线程互斥

  • 线程同步
    • 线程同步是一个宏观概念,在微观上包含线程的相互排斥和线程先后执行的约束问题。
    • 解决同步方式
      • 条件变量
      • 线程信号量
  • 线程互斥
    • 线程执行是相互排斥
    • 解决互斥方式
      • 互斥锁
      • 读写锁
      • 线程信号量

2.线程互斥案例—ATM取钱–没有使用互斥锁

头文件 account.h

#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__
//银行账户结构体
typedef struct{int code;//卡号double balance;//余额
}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

功能文件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;return a;
}
//销毁账户
void destory_account(Account *a){assert(a != NULL);free(a);//释放空间
}
//取款
double withdraw(Account *a,double amt){if(amt < 0 || amt > a->balance){return 0.0;}double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;return amt;
}
//存款
double deposit(Account *a,double amt){assert(a != NULL);if(amt < 0){return 0.0;}double balance = a->balance;sleep(1);balance += amt;a->balance = balance;return amt;
}
//查看账户余额
double get_balance(Account *a){assert(a != NULL);double balance = a->balance;return balanec;
}

执行文件 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;
}

程序输出结果:

在这里插入图片描述

girl和boy都从银行账户10001中取出了10000元,所以产生了错误。

3.线程互斥----互斥锁(互斥量)

  • 互斥锁是一种简单的加锁的方法,来控制对共享资源的访问。在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行访问。若其他线程希望上锁一个已经被上了互斥锁的资源,则该线程挂起,直到上锁的线程释放互斥锁为止。
  • 互斥锁数据类型
    • pthread_mutex_t

4.互斥锁创建和销毁

//锁的定义
pthreat_mutex_t  mutex;
//锁的初始化
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthreat_mutex_t *mutexattr);
//锁的销毁
int pthread_mutex_destroy(pthreat_mutex_t *mutex);
返回:成功返回 0  否则返回错误编号
  • 参数
    • mutex :互斥锁
    • mutexattr:互斥锁创建方式 或者可以传入锁的属性后面会有
      • PTHREAD_MUTEX_INITIALIZER(默认) 创建快速互斥锁 第一次上锁成功,第二次上锁会阻塞
      • PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 创建递归互斥锁 第一次上锁成功,第二次以后上锁还是成功,内部计数
      • PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP 创建检错互斥锁 第一次上锁成功,第二次上锁会出错

5.互斥锁上锁和解锁

int pthread_mutex_lock(pthread_mutex_t *mutex); //上锁  上不了锁该线程就阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex); //上锁 上不了锁该线程就返回错误信息
int pthread_mutex_unlock(pthread_mutex_t *mutex); //释放锁
返回:成功返回 0 出错返回出错码
  • 参数
    • mutex :互斥锁

6.线程互斥案例—ATM取钱–使用互斥锁

头文件 account.h

#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__
#include 
//银行账户结构体
typedef struct{int code;//卡号double balance;//余额//定义一把互斥锁,用来对多线程操作//银行账户(共享资源)进行加锁保护/*建议互斥锁和账户(共享资源)绑定一起,用来锁定一个账户(共享资源)尽量不设置全局变量,否则肯能出现一把锁去锁几百个账户,导致并发性能降低*/pthread_mutex_t mutex;}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

功能文件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_mutex_init(&a->mutex,null);return a;
}
//销毁账户
void destory_account(Account *a){assert(a != NULL);//销毁互斥锁pthread_mutex_destroy(&a->mutex)free(a);//释放空间
}
//取款
double withdraw(Account *a,double amt){assert(a != NULL);//判断指针a是否为空/*从成功上锁,到释放锁之间对共享资源操作的区间称为临界区*/pthread_mutex_lock(&a->mutex);//对共享共享资源(账户)加锁//有线程执行到此时,账户加锁,当其他线程执行到此时,就会被堵塞if(amt < 0 || amt > a->balance){//释放互斥锁pthread_mutex_unlock(&a->mutex);return 0.0;}double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;//释放互斥锁pthread_mutex_unlock(&a->mutex);return amt;
}
//存款
double deposit(Account *a,double amt){assert(a != NULL);pthread_mutex_lock(&a->mutex)//对共享共享资源(账户)加锁if(amt < 0){//释放互斥锁pthread_mutex_unlock(&a->mutex);return 0.0;}double balance = a->balance;sleep(1);balance += amt;a->balance = balance;//释放互斥锁pthread_mutex_unlock(&a->mutex);return amt;
}
//查看账户余额
double get_balance(Account *a){assert(a != NULL);pthread_mutex_lock(&a->mutex)//对共享共享资源(账户)加锁double balance = a->balance;//释放互斥锁pthread_mutex_unlock(&a->mutex);return balanec;
}

执行文件 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;
}

程序运行结果:

在这里插入图片描述

7.互斥锁属性创建和销毁

//互斥锁属性的初始化
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
//互斥锁属性的销毁
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
  • 参数
    • attr:互斥锁属性定义

8.互斥锁属性之一—进程共享属性操作

//restrict用于限定和约束指针,表示这个指针只访问这块内存的唯一方式
//共享属性获取,pshared用于存放获取的共享属性
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr,int *restrict pshared);
//共享属性设置,pshared用于指定共享属性
int pthread_mutexattr_setpshared(const pthread_mutexattr_t *attr,int pshared);
返回:成功返回0,出错返回错误编号
  • 参数
    • attr:互斥锁属性
    • pshared:进程共享属性
      • PTHREAD_PROCESS_PRIVATE(默认情况) 锁只能用于一个进程内部的两个进程进行互斥
      • PTHREAD_PROCESS_SHARED 锁可以用于两个不同进程中的线程进行互斥

9.互斥锁属性之一----互斥锁类型操作

//restrict用于限定和约束指针,表示这个指针只访问这块内存的唯一方式
//共享属性获取,pshared用于存放获取的共享属性
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr,int *restrict type);
//共享属性设置,pshared用于指定共享属性
int pthread_mutexattr_settype(const pthread_mutexattr_t *attr,int type);
返回:成功返回0,出错返回错误编号
  • 参数
    • attr:互斥锁属性
    • type:互斥锁类型
      • 标准互斥锁:PTHREAD_MUTEX_NORMAL 第一次上锁成功,第二次上锁会阻塞
      • 递归互斥锁:PTHREAD_MUTEX_RECURSIVE 第一次上锁成功,第二次以后上锁还是成功,内部计数
      • 检错互斥锁:PTHREAD_MUTEX_ERRORCHECK 第一次上锁成功,第二次上锁会出错
      • 默认互斥锁:PTHREAD_MUTEX_DEFAULT(同标准互斥锁)

10.代码了解互斥锁属性

#include 
#include 
#include 
#include int main(int argc,char *argv[]){//定义锁pthread_mutex_t mutex;if(argc<2){printf("-usage:%s [error|normal|recursive]/n",argv[0]);exit(1);}//定义互斥锁属性pthread_mutexatte_t  mutexattr;//初始化互斥锁属性pthread_mutexattr_init(&mutexattr);/*strcmp函数用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1str2,则返回正数。*/if(!strcmp(argv[1]),"error"){//设置互斥锁类型---检错型pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_ERRORCHECK);}else if(!strcmp(argv[1],"normal")){//设置互斥锁类型---标准型pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_NORMAL);}else if(!strcmp(argv[1],"recursive")){//设置互斥锁类型---递归型pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_RECURSIVE );}//锁的初始化,第一个参数传入锁,第二个参数可以指定,也可以传入锁的属性,pthread_mutex_init(&mutex,&mutexattr)//上锁if(pthread_mutex_lock(&mutex) !=0){printf("lock failure\n");}else{printf("lock sucess\n");}//第二次上锁if(pthread_mutex_lock(&mutex) !=0){printf("lock failure\n");}else{printf("lock sucess\n");}//上了两次锁,解锁两次pthread_mutex_unlock(&mutex);pthread_mutex_unlock(&mutex);//销毁属性pthread_mutexattr_destroy(&mutexattr);
}

程序运行结果:

​ 选择normal,锁的类型是普通型,第一次上锁成功,第二次失败,程序阻塞

​ 选择recursive,锁的类型是递归性,第一次上锁成功, 第二次也成功,只是内部计数。

​ 选择error,锁的类型是检错型,第一次上锁成功,第二次失败,程序报错
在这里插入图片描述
在这里插入图片描述

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...