Linux多线程C++版(五) 线程同步和线程互斥

news2024/10/6 0:32:20

目录

        • 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 <malloc.h>
#include <assert.h>
#include <string.h>

//创建账户
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 <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//操作者的结构体
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 <pthread.h>
//银行账户结构体
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 <malloc.h>
#include <assert.h>
#include <string.h>

//创建账户
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 <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//操作者的结构体
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 <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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,则返回零;
    	若str1<str2,则返回负数;
    	若str1>str2,则返回正数。
    */
    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,锁的类型是检错型,第一次上锁成功,第二次失败,程序报错
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37519.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]计算机毕业设计JAVA人口老龄化社区服务与管理平台

[附源码]计算机毕业设计JAVA人口老龄化社区服务与管理平台 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; …

Apollo 应用与源码分析:Monitor监控 - Monitor_managerecurrent_runner分析

目录 monitor_manager 分析 结构分析 单例模式宏定义 描述现在的系统状态 HMI上显示的状态信息 仿真判断状态 判断是不是自动驾驶状态 日志缓存 当前node 所有monitor 的reader 管理map 开启一次监控 start frame分析 end frame分析 recurrent_runner 分析 结构…

计算机键盘用途及快捷键

用途&#xff1a; 电脑键盘上有那么多按键&#xff0c;到底都有什么作用呢&#xff1f; 几个重要的按键&#xff0c;一起来了解一下吧。 最上面一排&#xff1a; F1帮助 F2改名 F3搜索 F4地址 F5刷新 F6切换 F10菜单 1、键盘中间区域的所有输入按键。 一共是26个英文字母…

初次接触氛围系统架构,聊聊我这三个月的理解

本文主要介绍了作者对于氛围中心的业务理解。从氛围的概念出发&#xff0c;阐述了氛围系统的必要性&#xff0c;然后展示了配置端的数据写入、调用端的配置读取等氛围系统的架构细节&#xff0c;最后作者提出了一些对于氛围中心未来的想法和思考。概述▐ 氛围的概念氛围是能够…

V5.1.1,新版发布|软件安全大于一切

主要内容&#xff1a;本次版本除了常规的BUG修复&#xff0c;最重要的是对系统安全全面升级&#xff0c;加强了系统安全检测机制&#xff0c;更新了Thinkphp核心版本&#xff0c;强化了密码等安全。 本次新增了在线用户&#xff0c;支持在线用户下强制下线处理。 本次新增的超级…

java项目-第161期ssm弹幕视频网站系统_ssm毕业设计_计算机毕业设计

java项目-第161期ssm弹幕视频网站系统_ssm毕业设计_计算机毕业设计 【源码请到资源专栏下载】 今天分享的项目是《ssm弹幕视频网站》 该项目分为2个角色&#xff0c;管理员、用户。 用户可以浏览前台视频信息、商品信息&#xff0c;并且可以进行购买。 管理员角色拥有的权限最…

基于jquery 实现导航条高亮显示的两种方法

本篇文章是基于jquery实现导航菜单高亮显示&#xff0c;当点击不同导航菜单实现当前点击的菜单是高亮的&#xff0c;有需要的朋友可以关注下本文 实现原理&#xff1a;当选中当前元素时&#xff0c;给当前元素添加样式&#xff0c;同级元素移除样式。 点击不同的导航菜单实现…

基于SpringBoot的校园志愿者管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SpringBoot 前端&#xff1a;HTML、Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#…

Java23种设计模式之第三弹-工厂模式

说起工厂&#xff0c;我们第一反应是制作什么东西的吧~。在现实生活中&#xff0c;工厂 &#xff0c; 就是用于生成一些特定事物的厂商。 回到我们此处说的工厂模式上&#xff0c;什么是工厂模式呢 &#xff0c; 顾名思义&#xff0c;就是生成我们的对象的类就会称成为工厂。 …

机器学习模型与backtrader框架整合

原创文章第116篇&#xff0c;专注“个人成长与财富自由、世界运作的逻辑&#xff0c; AI量化投资”。 北京疫情似乎还没有到拐点&#xff0c;但这三天结束后应该会到来。 今天重点说说&#xff0c;机器学习模型整合到我们的回测框架中&#xff0c;并与backtrader连接起来回测…

【Python模块】logging 日志模块

当入门一门语言时&#xff0c;最简单最直观的打印日志信息方式就是使用 print() 函数了&#xff0c;而这毕竟是自己练习和测试才会这样做。当参与项目时一定会去使用日志模块实现日志信息的打印和记录&#xff0c;而 Python 提供了内置的日志模块 logging&#xff0c;有必要深入…

解决每次打开pycharm都特别慢的几个方法

Python编写时&#xff0c;通常都会用vscode和pycharm两个工具&#xff0c;使用过程中&#xff0c;发现每次打开pycharm都特别特别慢&#xff0c;有时候要等十来分钟。相信大家可能都有遇到一样的情况&#xff0c;所以分享我自己的解决方法给大家参考。 1&#xff0c;每次需要关…

[附源码]java毕业设计置地房屋租赁信息系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

嵌入式驱动初级-中断

文章目录前言一、Linux 中断 API 函数二、Linux 中断实现三、中断上半部与下半部四、下半部机制之tasklet ---- 基于软中断五、按键中断下半部机制之tasklet六、下半部机制之workqueue ----- 基于内核线程七、按键中断下半部机制之workqueue前言 记录嵌入式驱动学习笔记 一、Li…

一文带你深入理解【Java基础】· 枚举类

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

Vision Transformer这两年

作者&#xff5c;Maximilian Schambach OneFlow编译 翻译&#xff5c;胡燕君、杨婷 在NLP领域取得巨大成功后&#xff0c;Transformer架构在计算机视觉方面的作用日渐凸显&#xff0c;成为越来越普遍的CV工具。自2020年10月Vision Transformer模型推出以来&#xff0c;人们开始…

《研究生学术与职业素养讲座》第七讲~第九讲作业答案

第七讲 八千里路云和月—2015年意大利米兰世博会中国馆设计 填空题&#xff08;1分&#xff09;单选题&#xff08;1分&#xff09;判断题&#xff08;1分&#xff09;多选题&#xff08;2分&#xff09;第八讲 从纳米研究看工程创新 填空题&#xff08;1分&#xff09;单…

干货 | 一条语句更新多个表

众所周知&#xff0c;多个服务器命中会减慢应用程序的速度。出于这个原因&#xff0c;开发人员致力于找尋使用最少的语句更新数据的最有效方法。事实证明&#xff0c;SQL UPDATE 语句确实支持使用以下语法设置多个表的字段&#xff1a; UPDATE table1, table2, ...SET column1…

2022-11-14 西安 activiti工作流(01)

语言确实有其局限性&#xff0c;但我相信:一件值得做的事情即使做的不怎么样也是值得的! 概念 1.流程审批以前的实现方式 在没有专门的工作流引擎之前&#xff0c;为了实现流程控制&#xff0c;通常的做法就是采用状态字段的值来跟踪流程的变化情况。通过状态字段的取值来决定…

【数据结构】二叉树优先级队列——堆

文章目录1. 树的概念及结构1.1 树的相关概念1.2 树的表示2. 二叉树的概念及其结构2.1 二叉树的概念2.2 特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储结构3. 堆3.1 堆的概念及结构3.2 堆的实现3.2.1 堆的创建3.2.2 堆的插入3.2.3 堆的向上调整算法3.2.4 堆的删除3.2.5 堆的向下…