龙空技术网

Linux多线程C++版(六) 线程同步和互斥 —— ATM机取钱案例

烦人的星辰 115

前言:

而今你们对“c语言atm机程序”可能比较看重,兄弟们都想要知道一些“c语言atm机程序”的相关知识。那么小编在网上收集了一些关于“c语言atm机程序””的相关资讯,希望朋友们能喜欢,朋友们快快来了解一下吧!

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_t4.互斥锁创建和销毁

//锁的定义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,锁的类型是检错型,第一次上锁成功,第二次失败,程序报错

标签: #c语言atm机程序