我有一个银行线程和4台ATM机.txt文件。(atm_0_input_file.txt - atm_4_input_file.txt)。
每个文件都是一个线程,银行是一个线程。当文件被读取时,程序结束。银行输出账户的当前状态(id,amount,pw)。
问题:
我目前正在获得(如果使用所有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文件
答案 0 :(得分:3)
我知道它被标记为c,但自从你在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