bank,4 atm machine输入txt文件,用信号量同步它们之间的信息

时间:2017-06-11 02:04:58

标签: c file unix centos semaphore

我有一个银行线程和4台ATM机.txt文件。(atm_0_input_file.txt - atm_4_input_file.txt)。

每个文件都是一个线程,银行是一个线程。当文件被读取时,程序结束。银行输出账户的当前状态(id,amount,pw)。

问题:

同步效果不好,我期待这样的输出: enter image description here

我目前正在获得(如果使用所有4个文件)只有一次银行的输出。顺便说一句,如果我只使用一个文件或两个文件,我会获得银行的2个输出。我正在尝试实现银行的至少3-4个输出,我正在为每个文件使用usleep(100000)并为银行使用sleep(3)。

另外,我正在使用系统调用来读取文件,读取每个字节并保存到字符串,然后逐字节解析。

嗯,这是代码。我在虚拟机上运行CentOS,因此我无法调试这750行代码。

我希望你能帮助我,我在24小时不停地工作,感到无助......

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>

#define BUF_SIZE  8192
sem_t sem_log;
sem_t sem_rc;
sem_t sem_db;
int rc;
int n, numOfFiles;

char* logName = "log.txt";
int logDescriptor;

typedef struct Account{
    int  id;
    int  password;
    int  balance;
    struct Account* next;
    struct Account* prev;
    sem_t sem_rc;
    sem_t sem_db;
    int rc;
}*acc;

acc head ,SaveHead;

typedef struct fileATM{
    char fileName[50];
    int fileNum;
}*fileAtm;

void parseMessage(char *msg, char tempId[20],char tempPw[20],char tempAmount[20],char temp2Id[20])
{
int i = 0;
int j = 0, k = 0, p = 0, h = 0;
int count = 0;
    while(msg[i] != '\0')
    {
     if(msg[i] == ' '){
        count++;
     }
     i++;
     switch(count){
      case 1: {tempId[j] = msg[i]; j++; break;}
      case 2: {tempPw[k] = msg[i]; k++; break;}
      case 3: {tempAmount[p] = msg[i]; p++; break;}
      case 4: {temp2Id[h] = msg[i]; h++; break;}
    }
    //i++;
    }
strcat(tempId, "\0");
strcat(tempPw, "\0");
strcat(tempAmount, "\0");
strcat(temp2Id, "\0");
}

//*finish: Update log.txt and lock it
void WriteLog(char* msg){
 sem_wait(&sem_log);
 strcat(msg,"\n");
 write(logDescriptor,msg,strlen(msg));
 sem_post(&sem_log);
}

void beforeReadAcc(acc temp)
{
    sem_wait(&temp->sem_rc);
    temp->rc++; //inc # of readers
    if(temp->rc==1)//sem_wait(&temp->sem_db);
    sem_post(&temp->sem_rc);
}

void beforeRead()
{
    sem_wait(&sem_rc);
    rc++; //inc # of readers
    if(rc==1)sem_wait(&sem_db);
    sem_post(&sem_rc);
}

void afterRead()
{
    sem_wait(&sem_rc);
    rc--; //dec # of readers
    if(rc==0)sem_post(&sem_db); /*last reader*/
    sem_post(&sem_rc);
}

void afterReadAcc(acc temp)
{
    sem_wait(&temp->sem_rc);
    temp->rc--; //dec # of readers
    if(temp->rc==0)sem_post(&temp->sem_db); /*last reader*/
    sem_post(&temp->sem_rc);
}


int doesAccExists(int id)
{
    //BLOCK DB
 //char* msgLog;
 acc temp = (acc)malloc(sizeof(struct Account));
 temp = head;
 beforeRead();
if(temp != NULL){
 while(temp != NULL)
    {
        sem_wait(&(temp->sem_db));
     if(temp->id == id){
         //RELEASE DB TWICE
         sem_post(&(temp->sem_db));
         afterRead();
        return 1;
     }
      sem_post(&(temp->sem_db));
      temp = temp->next;
    }
}

     afterRead();
    return 0;
}

void TransferAccount(fileAtm fileName, char* msg)
{
printf("\n Transfering to account... %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
int found = 0;
strcat(msg,"\0");
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, temp2Id, tempAmount);

    acc tempDest = (acc)malloc(sizeof(struct Account));
    acc temp2 = (acc)malloc(sizeof(struct Account));
    temp2=SaveHead;
    tempDest = temp2;
    printf(" %d", doesAccExists(atoi(tempId)));
    printf("%d %d",atoi(tempId),atoi(temp2Id) );
    if((doesAccExists(atoi(tempId)) == 1) && (found=doesAccExists(atoi(temp2Id)) == 1)){
    while(temp2 != NULL)
    {
        sem_wait(&(temp2->sem_db)); 
        if(temp2->id == atoi(tempId))
        {
            if(temp2->password == atoi(tempPw))
           {
               if(temp2->balance > atoi(tempAmount)){

                 temp2->balance -= atoi(tempAmount);
                 //Find destination id
                 sem_post(&(temp2->sem_db));
                 while(tempDest != NULL)
                 {
                     sem_wait(&(tempDest->sem_db));
                    if(tempDest->id == atoi(temp2Id)){
                      tempDest->balance += atoi(tempAmount);
                      found = 1;
                      sem_post(&(tempDest->sem_db));
                      break;
                    }
                    sem_post(&(tempDest->sem_db));
                    tempDest = tempDest->next;
                 }
                 beforeReadAcc(temp2);
                 sprintf(msgLog, "<ATM ID: %d>: Transfer <%d> from account <%d> to account <%d> new balance is <%d> new target account balance is <%d>",  fileName->fileNum, atoi(tempAmount),temp2->id, tempDest->id, temp2->balance,tempDest->balance);
                 afterReadAcc(temp2);
                 WriteLog(msgLog);
                 return;
               }
           }
          else
          {
          sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed  - password for id <%d> is incorrect",  fileName->fileNum, atoi(tempId));
          WriteLog(msgLog);
          sem_post(&(temp2->sem_db));
           return;
          }
       }
       sem_post(&(temp2->sem_db));
       temp2 = temp2->next;
     }//end while
    }
    else
    {
         if (found == 0)
         { //Did not found destination account
            sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist", fileName->fileNum,atoi(temp2Id));
            WriteLog(msgLog); return;
         }  
        sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist",  fileName->fileNum,atoi(tempId));
        WriteLog(msgLog);
        return;
    }
}


void CloseAccount(fileAtm fileName, char* msg)
{
printf("\n Closing account.. %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
int temp2Bal;
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);
    acc temp2 = (acc)malloc(sizeof(struct Account));
    acc temp3 = (acc)malloc(sizeof(struct Account));
    sem_init(&(temp3->sem_db),0,1);
    temp2=SaveHead;

    printf(" %d", doesAccExists(atoi(tempId)));
    if(doesAccExists(atoi(tempId)) == 1){
    while(temp2 != NULL)
    {
        sem_wait(&(temp2->sem_db));
        if(temp2->id == atoi(tempId))
        {
            if(temp2->password == atoi(tempPw))
           {
             beforeRead();
            //If it's the only item'
            //Lock DB
            //sem_wait(&sem_db);
            if(temp2->prev == NULL && temp2->next == NULL) {
                //Lock and Release temp2,temp3
                beforeReadAcc(temp2);
                sem_wait(&(temp3->sem_db));
                temp3->balance = temp2->balance;
                sem_post(&(temp3->sem_db));

                sem_post(&(temp2->sem_db));
                afterReadAcc(temp2);
                free(temp2);
                //Lock and release SaveHead,Head
                SaveHead=head=NULL; 
                }
            //If it's head and there're more items on da list
            else if(temp2->prev == NULL && temp2->next != NULL)
            {
                beforeReadAcc(temp2);
              //sem_wait(&(SaveHead->sem_db));  
              SaveHead = temp2->next;
              //sem_post(&(SaveHead->sem_db));

              sem_wait(&(temp2->next->sem_db));
              temp2->next->prev = NULL;
              sem_post(&(temp2->next->sem_db));


              sem_wait(&(temp3->sem_db));
              temp3->balance = temp2->balance;
              sem_post(&(temp3->sem_db));

              sem_post(&(temp2->sem_db));
              afterReadAcc(temp2);
              free(temp2);
            }
            //Delete from middle in the list
            else if(temp2->prev != NULL && temp2->next != NULL)
            {
                beforeReadAcc(temp2);
                sem_wait(&(temp2->prev->sem_db));
                temp2->prev->next = temp2->next;
                sem_post(&(temp2->prev->sem_db));

                sem_wait(&(temp2->next->sem_db));
                temp2->next->prev = temp2->prev;
                sem_post(&(temp2->next->sem_db));

                sem_wait(&(temp3->sem_db));
                temp3->balance = temp2->balance;
                sem_post(&(temp3->sem_db));

                sem_post(&(temp2->sem_db));
                afterReadAcc(temp2);
                free(temp2);
            }
            //Delete from the end
            else if(temp2->prev != NULL && temp2->next == NULL)
            {
                beforeReadAcc(temp2);
                sem_wait(&(temp2->prev->sem_db));
                temp2->prev->next = NULL;
                sem_post(&(temp2->prev->sem_db));

                sem_wait(&(temp3->sem_db));
                temp3->balance = temp2->balance;
                sem_post(&(temp3->sem_db));

                sem_post(&(temp2->sem_db));
                afterReadAcc(temp2);
                free(temp2);
            }
            //Release DB
            //sem_post(&sem_db);
            afterRead();
            sprintf(msgLog, "<ATM ID: %d>: Account <%d> is now closed. Balance was <%d>", fileName->fileNum, atoi(tempId), temp3->balance);
            WriteLog(msgLog); return;
           }
          else
          {
          sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed  - password for id <%d> is incorrect",  fileName->fileNum, atoi(tempId));
          WriteLog(msgLog); return;
          }
       }
       sem_post(&(temp2->sem_db));
       temp2 = temp2->next;
     }
    }
    else
    {
        sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist",  fileName->fileNum,atoi(tempId));
        WriteLog(msgLog);
        return;
    }
}


void depositAccount(fileAtm fileName, char* msg)
{
printf("\n Depositing to account.. %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
int SaveId;
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);

    acc temp2 = (acc)malloc(sizeof(struct Account));
    temp2=SaveHead;

    printf(" %d", doesAccExists(atoi(tempId)));
    if(doesAccExists(atoi(tempId)) == 1){
    sem_wait(&(temp2->sem_db)); 
    while(temp2 != NULL)
    {
        if(temp2->id == atoi(tempId))
        {
            if(temp2->password == atoi(tempPw))
           {
            //Block User's DB'

            temp2->balance += atoi(tempAmount);
            //Release User's DB'
            sem_post(&(temp2->sem_db));
            beforeReadAcc(temp2);
            sprintf(msgLog, "<ATM ID: %d>: Account <%d> new balance is <%d> after <%d>$ was deposited",  fileName->fileNum, temp2->id, temp2->balance, atoi(tempAmount));
            afterReadAcc(temp2);
            WriteLog(msgLog);
            return;
           }
          else
          {
          sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed  - password for id <%d> is incorrect",  fileName->fileNum, temp2->id);
          WriteLog(msgLog); return;
          }
       }
       sem_post(&(temp2->sem_db));
       temp2 = temp2->next;    
    }//end while
    }
    else
    {
        sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist",  fileName->fileNum,atoi(tempId));
        WriteLog(msgLog);
        return;
    }
}

void Withdrawl(fileAtm fileName, char* msg)
{
printf("\n Withdrawl from account.. %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);

    acc temp2 = (acc)malloc(sizeof(struct Account));
    temp2=SaveHead;

    printf(" %d", doesAccExists(atoi(tempId)));
    if(doesAccExists(atoi(tempId)) == 1){
    sem_wait(&(temp2->sem_db)); 
    while(temp2 != NULL)
    {
        if(temp2->id == atoi(tempId)){
            if(temp2->password == atoi(tempPw)){
            //Block User's DB'
             if(temp2->balance > atoi(tempAmount))
             {
                 temp2->balance -= atoi(tempAmount);
                 //Release User's DB'
                 sem_post(&(temp2->sem_db));
                 //Lock acc
                 beforeReadAcc(temp2);
                 sprintf(msgLog, "<ATM ID: %d>: Account <%d> new balance is <%d> after <%d>$ was Withdrawl",  fileName->fileNum, temp2->id, temp2->balance, atoi(tempAmount));
                 //Release acc
                 afterReadAcc(temp2);
                 WriteLog(msgLog);
                 return;
             }
             else
             {
                 sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed account id <%d> balance is lower than <%d> ",  fileName->fileNum, temp2->id, atoi(tempAmount));
                 WriteLog(msgLog); return;
             }
           }
          else
          {
          sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed  - password for id <%d> is incorrect",  fileName->fileNum, atoi(tempId));
          WriteLog(msgLog); return;
          }
       }
       sem_post(&(temp2->sem_db));
       temp2 = temp2->next;
    }
    }
    else
    {
        sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist",  fileName->fileNum,atoi(tempId));
        WriteLog(msgLog);
        return;
    }
}

void Balance(fileAtm fileName, char* msg){
printf("\n Balance from account.. %s\n", msg);
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);

    acc temp2 = (acc)malloc(sizeof(struct Account));
    temp2=SaveHead;

    printf(" %d", doesAccExists(atoi(tempId)));
    if(doesAccExists(atoi(tempId)) == 1){
    while(temp2 != NULL)
    {
        if(temp2->id == atoi(tempId))
        {
            if(temp2->password == atoi(tempPw))
             {
                 //lock user
                 beforeReadAcc(temp2);
                 sprintf(msgLog, "<ATM ID: %d>: Account <%d> new balance is <%d> ",  fileName->fileNum, temp2->id, temp2->balance);
                 //release user
                 afterReadAcc(temp2);
                 WriteLog(msgLog); return;
             }
             else
             {
                 sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed - password for account id <%d> is incorrect",  fileName->fileNum, temp2->id);
                 WriteLog(msgLog); return;
             }
        }
            temp2 = temp2->next;
    }
 }
    else
    {
        sprintf(msgLog, "<ATM ID: %d>: Your transaction failed - Account id <%d> doesn't exist",  fileName->fileNum,temp2->id);
        WriteLog(msgLog);
        return;
    }
}


void openNewAccount(fileAtm fileName, char* msg){
printf("\n Opening account.. %s", msg);
int i = 0;
char tempId[20];
char tempPw[20];
char tempAmount[20];
char temp2Id[20];
acc temp = (acc)malloc(sizeof(struct Account));
int length = strlen(msg);
char msgLog[400];
strcat(msg,"\0");
//Parse the message from file into 3 strings

parseMessage(msg, tempId, tempPw, tempAmount, temp2Id);

    //Translate id,pw,amount from string to int
    temp->id = atoi(tempId);
    temp->password = atoi(tempPw);
    temp->balance = atoi(tempAmount);

    acc temp2 = (acc)malloc(sizeof(struct Account));
    acc tempTest = (acc)malloc(sizeof(struct Account));
    temp2 = head;
    tempTest = head;

    //Search if id exists
    beforeRead();
    while(temp2 != NULL)
    {
        sem_wait(&temp2->sem_db);
        if(temp2->id == temp->id){
            sem_post(&temp2->sem_db);
            sprintf(msgLog, "Error <ATM ID: %d>: Your transaction failed  - account with the same id exists", fileName->fileNum);
            WriteLog(msgLog);
            return;
        }
        else{
            sem_post(&temp2->sem_db);
            temp2 = temp2->next;
        }
    }
    afterRead();

    //Start opening account - lock db
    sem_wait(&sem_db);

    //Inititalize list, append its head
    if(head == NULL){
    //List is empty

        head = (acc) malloc(sizeof(struct Account));
        sem_init(&(head->sem_db), 0, 1);
        sem_init(&(head->sem_rc), 0, 1);

        //Lock user's DB'
        sem_wait(&(head->sem_db));
        head->id = temp->id;
        head->password = temp->password;
        head->balance = temp->balance;
        head->prev = NULL;
        head->next = NULL;
        head->rc = 0;

        SaveHead = head;

        //Release user's DB'
        sem_post(&(head->sem_db));
    printf("\n**Account: id %d, pw %d, amount %d\n", head->id, head->password, head->balance);
    //Update log
    sem_wait(&head->sem_db);
    sprintf(msgLog, "<ATM ID: %d> New account id is <%d> with password <%d> and initial balance <%d>,", fileName->fileNum, head->id, head->password, head->balance);
    sem_post(&head->sem_db);
    }
    else
    {
    //List is not empty
    //Lock user's DB'
     temp->rc = 0;
     sem_init(&(temp->sem_db), 0, 1);
     sem_init(&(temp->sem_rc), 0, 1);
     temp->prev=NULL;
     temp->next=NULL;
     //sem_post(&(temp->sem_db));
    //Find tail

    while(head->next != NULL)
    { 
        sem_wait(&(head->sem_db));
        tempTest = head;
        head=head->next;
        sem_post(&(tempTest->sem_db));
    }

    sem_wait(&(temp->sem_db));
    temp->prev=head;
    sem_post(&(temp->sem_db));

    sem_wait(&(head->sem_db));
    head->next=temp;
    sem_post(&(head->sem_db));

    head=SaveHead;

    sem_wait(&temp->sem_db);
    printf("\n***Account: id %d, pw %d, amount %d\n", temp->id, temp->password, temp->balance);
    sem_post(&temp->sem_db);

    //Update log
    sem_wait(&temp->sem_db);
    sprintf(msgLog, "<ATM ID: %d> New account id is <%d> with password <%d> and initial balance <%d>,", fileName->fileNum, temp->id, temp->password, temp-> balance);
    sem_post(&temp->sem_db);
    }

    WriteLog(msgLog);

    //Account created - Release DB
    sem_post(&sem_db);


}

//*Opens file for every ATM
void* openFile(void* args){
  //test:

  //To add later: while(true) { sleep(100); do next file's line }
  //Open file
  fileAtm getFile = (fileAtm) args;
  printf("\n %s ", getFile->fileName);
  int ret_in, in1,file;
  char buffer1[BUF_SIZE];
  char myLetter;
  int count = 0;
  int i = 0, j = 0,k;

  char msg[30][30];
 file = open(getFile->fileName,O_RDONLY,0644);
 //Test:
 // printf("\n %s ", getFile->fileName);
 //msg = "test";

if((ret_in = read (file, &buffer1, BUF_SIZE)) > 0)
{
   for(i=0,k=0; i<ret_in; i++,k++)
    {
     if(buffer1[i]=='\n')
     {j++; count++; k=0;i++;}

      msg[j][k] = buffer1[i];

    }
}
printf("# of lines: %d %c", count, msg[1][0]);
  //Here we call the relevant function of the msg

for(j=0; j<=count;j++)
{
   switch (msg[j][0]){

   case 'O': {openNewAccount(getFile,&(msg[j][0]));
            // printf("\ntest :%c ",msg[j][0]);
             break;}
   case 'D': {printf("\ntest :%c ",msg[j][0]);depositAccount(getFile, &(msg[j][0])); break; }
   case 'W': {printf("\ntest :%c ",msg[j][0]);Withdrawl(getFile, &(msg[j][0])); break; }
   case 'B': {printf("\ntest :%c ",msg[j][0]);Balance(getFile, &(msg[j][0])); break;}
   case 'Q': {printf("\ntest :%c ",msg[j][0]);CloseAccount(getFile, &(msg[j][0])); break;}
   case 'T': {printf("\ntest :%c ",msg[j][0]);TransferAccount(getFile, &(msg[j][0])); break; }
   }
}
usleep(100000);
numOfFiles++;
close(file);

}


void* bankLoop(void* nothing)
{

    int totalAmount = 0;
    acc temp = (acc)malloc(sizeof(struct Account));
    acc temp2 = (acc)malloc(sizeof(struct Account));
    while(1)
    {
        sleep(1);
        temp = head;
        beforeRead();
        printf("\n*******************************\n");
        printf("\nBank status: \n");
        while(temp != NULL)
        {
            beforeReadAcc(temp);
            printf("\nAccount: %d, Account password: %d ,Balance: %d$",temp->id, temp->password, temp->balance);
            totalAmount+= temp->balance;
            temp2 = temp;
            temp = temp->next;
            afterReadAcc(temp2);
        }

        //Print and Reset bank's balance to zero
        printf("\nBank's balance: %d\n", totalAmount);
        if(totalAmount != 0) totalAmount = 0;
        printf("\n*********************************\n");
        afterRead();
        if(numOfFiles == n)
            break;
    }


}


int main(void)
{
int i,a;
pthread_t bank;
char fileName[50];
head = NULL;
numOfFiles = 0;
//Init semaphores
sem_init(&sem_log, 0, 1);
sem_init(&sem_rc,0,1);
sem_init(&sem_db,0,1);

printf("Please enter the number of ATMs you want: \n");
scanf("%d", &n);

//Open log file in background
logDescriptor = open(logName,O_WRONLY|O_CREAT,0644);


//Create bank thread
pthread_create( &bank, NULL, bankLoop, NULL);

//Initialization FILES threads
fileAtm files = (fileAtm)malloc(n*sizeof(struct fileATM));
//Files initialization
for(i = 0; i < n; i++){
    sprintf(fileName, "ATM_%d_input_file.txt", i);
    strcpy((files[i]).fileName,fileName);
    (files[i]).fileNum = i;
}
//Testing:
for(i=0; i<n;i++)
printf("\n%s %d", (files[i]).fileName, (files[i]).fileNum);

//Threads (ATM) initialization
pthread_t* atmThreads = (pthread_t*)malloc(n*sizeof(pthread_t));


printf("test\n"); //TEST: check msg

//Create ATMs threads
for(i = 0; i < n; i++){
pthread_create ( &atmThreads[i] , NULL , openFile , (void*)&files[i]);
}

//Join bank thread
pthread_join(bank,NULL);

//Join ATM threads
for(i=0; i < n; i++)
pthread_join(atmThreads[i],NULL);


//scanf("%d",&a);
}

抱歉这750行代码,我希望有人有时间并有足够的勇气检查出来。

  

ATM_0_input_file.txt

O 1111 1111 123
O 4444 4444 598
T 4444 4444 2222 61
W 1111 1234 50
D 4444 4444 50
D 2222 0300 50
D 2222 0000 500
Q 1111 1111
Q 4444 4444
W 2234 2345 50
O 1212 4444 433
O 1432 4444 23
O 1165 4444 2
O 0986 4444
Q 1212 4444
Q 1432 4444
Q 1165 4444
Q 0986 4444
  

ATM_1_input_file.txt

O 1234 1234 60
D 1234 1234 50
W 1234 1234 20
D 3333 1234 20
T 1234 0000 2244 20
W 1234 1234 900
  

ATM_2_input_file.txt

D 1234 0000 60
O 3456 1234 20
D 3333 1234 20
T 1234 0000 2244 20
W 1234 0000 900
D 3456 1234 999
D 1234 0000 9
T 3456 1234 2244 203
Q 3333 1234
  

ATM_3_input_file.txt

O 7777 7777 1766
O 4444 4444 598
T 4444 4444 7777 5
T 7777 7777 4444 500
D 1111 1234 50
O 2222 1234 50
Q 1111 1111
Q 4444 4444
W 2234 2345 50

编辑:修正了警告。添加了.txt文件

1 个答案:

答案 0 :(得分:3)

我知道它被标记为,但自从你在Lounge<C++>中发送垃圾邮件后,让我用C ++幽默你:

<强> Live On Coliru

#include <algorithm>
#include <functional>
#include <atomic>
#include <fstream>
#include <iostream>
#include <list>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

// not thread-aware:
struct Bank {
    struct Account {
        int id;
        int password;
        int balance;

        Account(int id, int password = 0, int balance = 0)
            : id(id), password(password), balance(balance) { }

        bool operator==(Account const& other) const { return id == other.id; }
    };

    void remove(int id) {
        auto& acc = get_unverified(id);
        accounts.remove(acc);
    }

    void add(Account const& acc) {
        if (std::count(accounts.begin(), accounts.end(), acc.id))
            throw std::runtime_error("account with the same id exists"); // TODO include id?
        accounts.push_back(acc);
    }

    Account& get_unverified(int id) {
        auto it = std::find(accounts.begin(), accounts.end(), id);
        if (it != accounts.end())
            return *it;
        throw std::runtime_error("Account " + std::to_string(id) + " doesn't exist");
    }

    Account& get(int id, int password) {
        auto& acc = get_unverified(id);
        if (acc.password != password)
            throw std::runtime_error("Password for id <" + std::to_string(id) + "> is incorrect");
        return acc;
    }

    void status() const {
        std::cout << "*******************************\n";
        std::cout << "Bank status: \n";

        int totalAmount = 0;
        for (auto const &acc : accounts) {
            std::cout << "Account: " << acc.id << ", Account password: " << acc.password << ", Balance: " << acc.balance
                << "$\n";
            totalAmount += acc.balance;
        }

        // Print and Reset bank's balance to zero
        std::cout << "Bank's balance: " << totalAmount << "\n";
        std::cout << "*******************************\n";
    }

  private:
    std::list<Account> accounts;
};

// something to make access guarded:
template <typename T>
struct Locking {
    template <typename Op>
    void transactional(Op&& op) {
        std::lock_guard<std::mutex> lk(_mx);
        std::forward<Op>(op)(_value);
    }

  private:
    std::mutex _mx;
    T _value;
};

static void bankLoop(Locking<Bank>& safe, std::atomic_bool& keepRunning) {
    while (keepRunning) {
        std::this_thread::sleep_for(std::chrono::seconds(1));

        safe.transactional(std::mem_fn(&Bank::status));
    }
}

struct Log {
    Log() : ofs("log.txt") { } // TODO fix mode/fail on existing?

    void Write(std::string const &msg) {
        std::lock_guard<std::mutex> lk(_mx);
        ofs << msg << "\n";
    }

  private:
    std::mutex _mx;
    std::ofstream ofs;
} logDescriptor;

struct Atm {
    Locking<Bank>& safe;
    int const fileNum;

    void process(std::string const& fileName) {
        std::ifstream file(fileName);

        std::string line;
        while (std::getline(file, line)) {
            // totally made up this feature:
            if (line.empty()) {
                std::this_thread::sleep_for(std::chrono::milliseconds(200));
                continue;
            }

            switch (line[0]) {
                case 'O': openNewAccount(line); break;
                case 'D': depositAccount(line); break;
                case 'W': Withdrawl(line); break;
                case 'B': Balance(line); break;
                case 'Q': CloseAccount(line); break;
                case 'T': TransferAccount(line); break;
            }
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        file.close();
    }

  private:

    void TransferAccount(std::string const& msg) {
        std::cout << "Transfering to account... " << msg << "\n";

        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& source = bank.get(cmd.id, cmd.pw);
                auto& target = bank.get_unverified(cmd.id2);

                source.balance -= cmd.amount;
                target.balance += cmd.amount;

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Transfer <" << cmd.amount << "> "
                    << "from account <" << cmd.id << "> "
                    << "to account <" << cmd.id2 << "> "
                    << "new balance is <" << source.balance << "> "
                    << "new target account balance is <" << target.balance << ">";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void CloseAccount(std::string const& msg) {
        std::cout << "Closing account... " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& acc = bank.get(cmd.id, cmd.pw);
                auto const balance = acc.balance;

                bank.remove(acc.id);

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Account <" << cmd.id << "> is now closed. "
                    << "Balance was <" << balance << ">";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void depositAccount(std::string const& msg) {
        std::cout << "Depositing to account.. " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& acc = bank.get(cmd.id, cmd.pw);
                acc.balance += cmd.amount;

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Account <" << cmd.id << "> "
                    << "new balance is <" << acc.balance << "> "
                    << "after <" << cmd.amount << "> was deposited";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void Withdrawl(std::string const& msg) {
        std::cout << "Withdrawl from account.. " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& acc = bank.get(cmd.id, cmd.pw);
                acc.balance -= cmd.amount;

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Account <" << cmd.id << "> "
                    << "new balance is <" << acc.balance << "> "
                    << "after <" << cmd.amount << "> was Withdrawl";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void Balance(std::string const& msg) {
        std::cout << "Balance from account.. " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                auto& acc = bank.get(cmd.id, cmd.pw);

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "Account <" << cmd.id << "> "
                    << "new balance is <" << acc.balance << ">";
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

    void openNewAccount(std::string const& msg) {
        std::cout << "Opening account... " << msg << "\n";
        safe.transactional([=](Bank& bank) {
            std::ostringstream msgLog;
            try {
                auto const cmd = parseCommand(msg);
                Bank::Account const acc(cmd.id, cmd.pw, cmd.amount);
                bank.add(acc);

                msgLog 
                    << "<ATM ID: " << fileNum << ">: "
                    << "New account id is <" << acc.id << "> with passoword <" << acc.password << "> and initial balance <" << acc.balance << ">,"; // FIXME trailing comma
            } catch(std::exception const& e) {
                msgLog << "Error <ATM ID: " << fileNum << ">: Your transaction failed  - " << e.what();
            }
            logDescriptor.Write(msgLog.str());
        });
    }

  private:
    struct Cmd { int id=-1, pw=-1, amount=0, id2=-1; };
    static Cmd parseCommand(std::string const& msg) {
        Cmd result;
        char discard; // absorbs command character
        std::istringstream iss(msg);

        if (!(iss >> discard >> result.id >> result.pw))
            throw std::runtime_error("the command message is invalid");

        iss >> result.amount >> result.id2;

        return result;
    }
};

int main() {
    std::cout << "Please enter the number of ATMs you want: ";
    int n = 0;
    if (!(std::cin >> n))
        throw std::runtime_error("Input failed");

    // Create bank thread
    Locking<Bank> bank;
    std::atomic_bool keepRunning{true};
    std::thread bankThread(&bankLoop, std::ref(bank), std::ref(keepRunning));

    std::list<std::thread> atmThreads;
    for (int i = 0; i < n; i++) {
        atmThreads.emplace_back([&bank, i] {
            Atm atm { bank, i };
            atm.process("ATM_" + std::to_string(i) + "_input_file.txt");
        });
    }

    // Join ATM threads
    for (auto &atm : atmThreads)
        atm.join();

    // Join bank thread
    keepRunning = false;
    bankThread.join();
}

让我们用1个ATM文件测试它

O 123 8888 10
O 123 8888 10
W 123 8888 5
B 123 8888
B 123 7777

O 234 9999 20
D 234 9999 50
B 234 9999

W 123 8888 15
T 234 9999 30  123
Q 234 9999
Q 234 9999
B 123 what

打印

Please enter the number of ATMs you want: 1
Opening account... O 123 8888 10
Opening account... O 123 8888 10
Withdrawl from account.. W 123 8888 5
Balance from account.. B 123 8888
Balance from account.. B 123 7777
Opening account... O 234 9999 20
Depositing to account.. D 234 9999 50
Balance from account.. B 234 9999
Withdrawl from account.. W 123 8888 15
Transfering to account... T 234 9999 30  123
Closing account... Q 234 9999
Closing account... Q 234 9999
Balance from account.. B 123 what
*******************************
Bank status: 
Account: 123, Account password: 8888, Balance: 20$
Bank's balance: 20
*******************************

log.txt最终会像:

<ATM ID: 0>: New account id is <123> with passoword <8888> and initial balance <10>,
Error <ATM ID: 0>: Your transaction failed  - account with the same id exists
<ATM ID: 0>: Account <123> new balance is <5> after <5> was Withdrawl
<ATM ID: 0>: Account <123> new balance is <5>
Error <ATM ID: 0>: Your transaction failed  - Password for id <123> is incorrect
<ATM ID: 0>: New account id is <234> with passoword <9999> and initial balance <20>,
<ATM ID: 0>: Account <234> new balance is <70> after <50> was deposited
<ATM ID: 0>: Account <234> new balance is <70>
<ATM ID: 0>: Account <123> new balance is <-10> after <15> was Withdrawl
<ATM ID: 0>: Transfer <30> from account <234> to account <123> new balance is <40> new target account balance is <20>
<ATM ID: 0>: Account <234> is now closed. Balance was <40>
Error <ATM ID: 0>: Your transaction failed  - Account 234 doesn't exist
Error <ATM ID: 0>: Your transaction failed  - the command message is invalid