문제. 은행 잔고 프로그램을 멀티 쓰레드로 작성하시오.
- 은행 잔고는 전역 변수로 선언
- 은행 잔고를 조회하는 쓰레드는 3개 존재하고, 각 쓰레드는 은행 잔고를 1초에 한번씩 조회하여 출력하도록 쓰레드 구현
- 은행 잔고를 업데이트하는 쓰레드는 2개 존재하고, 1개 쓰레드는 은행 잔고를 1초에 1,000원씩 증가, 1개 쓰레드는 은행 잔고를 2초에 500원씩 감소하도록 구현
- 메인 쓰레드는 은행 잔고가 10,000원 이상이 되면 모든 쓰레드를 종료하고 프로그램 종료하도록 함
- mutex lock 사용하는 버전과 rwlock 사용하는 버전 2개를 작성
출력. 은행 잔고를 출력한다.
입력 예제. my_balance
출력 예제.
0원
1000원
1000원
1000원
500원
1500원
/* mutex lock ver. */
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h> //exit() malloc() free()
#include <errno.h> //errno
#include <string.h> //strerror_r()
#include <time.h> //nanosleep() clock_gettime()
enum { ERR_BUFSIZE = 64, NUM_CHECK = 3, NUM_UPDATE = 2,
MAX_BALANCE = 10000 };
static int balance;
static pthread_mutex_t mutex;
typedef struct DATA{
int count; //스레드 번호
int amount; //증감할 돈
} DATA;
void *Check(void *data);
void *Update(void *data);
int main()
{
size_t i;
balance = 0;
pthread_t th_check[NUM_CHECK]; //잔고 출력하는 스레드
pthread_t th_update[NUM_UPDATE]; //잔고 업데이트하는 스레드
DATA *data_ch;
data_ch = (struct DATA *)malloc(sizeof(struct DATA) * NUM_CHECK);
if (data_ch == NULL) {
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
DATA *data_up;
data_up = (struct DATA *)malloc(sizeof(struct DATA) * NUM_UPDATE);
if (data_up == NULL) {
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
if (pthread_mutex_init(&mutex, NULL) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
for (i = 0; i < NUM_CHECK; i++) {
data_ch[i].count = i; //스레드 0, 1, 2번
data_ch[i].amount = 0;
if (pthread_create(&th_check[i], NULL, Check, (void *)&data_ch[i]) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
}
for (i = 0; i < NUM_UPDATE; i++) {
data_up[i].count = i + NUM_CHECK; //스레드 3, 4번
if (i == 0) {
data_up[i].amount = 1000; //1초에 1000원씩 증가
}
else if (i == 1) {
data_up[i].amount = -500; //2초에 500원씩 감소
}
if (pthread_create(&th_update[i], NULL, Update, (void *)&data_up[i]) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
}
for (i = 0; i < NUM_CHECK; i++) {
if (pthread_join(th_check[i], (void **)NULL) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
}
for (i = 0; i < NUM_UPDATE; i++) {
if (pthread_join(th_update[i], (void **)NULL) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
}
if (pthread_mutex_destroy(&mutex) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
free(data_ch);
free(data_up);
return 0;
}
void *Check(void *data)
{
long deltaTime = 0;
struct timespec start = { 0, 0 };
struct timespec end = { 0, 0 };
struct timespec sleep = { 0, 010000000 }; //sec:0, nsec:01 => 0.01초
DATA d = *(DATA *)data;
while (1) {
if (clock_gettime(CLOCK_MONOTONIC, &start) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
//////////* mutex lock *//////////
if (pthread_mutex_lock(&mutex) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
if (balance >= MAX_BALANCE) {
if (pthread_mutex_unlock(&mutex) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
return NULL;
}
printf("스레드[%d] %d원\n", d.count, balance);
if (pthread_mutex_unlock(&mutex) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
///////////////////////////////////
while (1) {
//CLOCK_MONOTONIC: 두 이벤트의 시간차이를 알고자 할 때 사용됨
if (clock_gettime(CLOCK_MONOTONIC, &end) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
deltaTime = end.tv_sec - start.tv_sec;
//printf("delta %ld\n", deltaTime);
//시간차가 1초 이상이면 탈출
if (deltaTime >= 1) {
break;
}
if (nanosleep(&sleep, NULL) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
//sleep 시간만큼 또는 시그널을 받을 때까지 스레드를 블록시킴.
//만약 시그널에 의해 스레드가 깨어나고 2번째 인자가 NULL이 아니라면,
//2번째 인자에 남은 시간을 담아줌.
}
}
return NULL;
}
void *Update(void *data)
{
int deltaTime = 0;
struct timespec start = { 0, 0 };
struct timespec end = { 0, 0 };
struct timespec sleep = { 0, 010000000 }; //sec:0, nsec:01 => 0.01초
DATA d = *(DATA *)data;
while (1) {
if (clock_gettime(CLOCK_MONOTONIC, &start) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
//////////* mutex lock *//////////
if (pthread_mutex_lock(&mutex) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
if (balance >= MAX_BALANCE) {
if (pthread_mutex_unlock(&mutex) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
return NULL;
}
balance += d.amount;
if (d.amount > 0) {
printf("스레드[%d] +%d\n", d.count, d.amount);
}
else if (d.amount < 0) {
printf("스레드[%d] %d\n", d.count, d.amount);
}
if (pthread_mutex_unlock(&mutex) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
///////////////////////////////////
while (1) {
if (clock_gettime(CLOCK_MONOTONIC, &end) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
deltaTime = end.tv_sec - start.tv_sec;
//printf("delta %ld\n", deltaTime);
if (d.amount < 0 && deltaTime >= 2) {
break;
}
else if (d.amount > 0 && deltaTime >= 1) {
break;
}
if (nanosleep(&sleep, NULL) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
}
}
return NULL;
}
/* rwlock ver. */
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h> //exit() malloc() free()
#include <errno.h> //errno
#include <string.h> //strerror_r()
#include <time.h> //nanosleep() clock_gettime()
enum { ERR_BUFSIZE = 64, NUM_CHECK = 3, NUM_UPDATE = 2,
MAX_BALANCE = 10000};
static int balance;
static pthread_rwlock_t rwlock;
typedef struct DATA {
int count; //스레드 번호
int amount; //증감할 돈
} DATA;
void *Check(void *data);
void *Update(void *data);
int main()
{
size_t i;
balance = 0;
pthread_t th_check[NUM_CHECK]; //잔고 출력하는 스레드
pthread_t th_update[NUM_UPDATE]; //잔고 업데이트하는 스레드
DATA *data_ch;
data_ch = (struct DATA *)malloc(sizeof(struct DATA) * NUM_CHECK);
if (data_ch == NULL) {
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
DATA *data_up;
data_up = (struct DATA *)malloc(sizeof(struct DATA) * NUM_UPDATE);
if (data_up == NULL) {
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
if (pthread_rwlock_init(&rwlock, NULL) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
for (i = 0; i < NUM_CHECK; i++) {
data_ch[i].count = i; //스레드 0, 1, 2번
data_ch[i].amount = 0;
if (pthread_create(&th_check[i], NULL, Check, (void *)&data_ch[i]) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
}
for (i = 0; i < NUM_UPDATE; i++) {
data_up[i].count = i + NUM_CHECK; //스레드 3, 4번
if (i == 0) {
data_up[i].amount = 1000; //1초에 1000원씩 증가
}
else if (i == 1) {
data_up[i].amount = -500; //2초에 500원씩 감소
}
if (pthread_create(&th_update[i], NULL, Update, (void *)&data_up[i]) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
}
for (i = 0; i < NUM_CHECK; i++) {
if (pthread_join(th_check[i], (void **)NULL) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
}
for (i = 0; i < NUM_UPDATE; i++) {
if (pthread_join(th_update[i], (void **)NULL) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
}
if (pthread_rwlock_destroy(&rwlock) != 0) {
free(data_ch);
free(data_up);
fprintf(stderr, "errno[%d]", errno);
exit(EXIT_FAILURE);
}
free(data_ch);
free(data_up);
return 0;
}
void *Check(void *data)
{
long deltaTime = 0;
struct timespec start = { 0, 0 };
struct timespec end = { 0, 0 };
struct timespec sleep = { 0, 010000000 }; //sec:0, nsec:01 => 0.01초
DATA d = *(DATA *)data;
while (1) {
if (clock_gettime(CLOCK_MONOTONIC, &start) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
//////////* READ lock *//////////
if (pthread_rwlock_rdlock(&rwlock) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
if (balance >= MAX_BALANCE) {
if (pthread_rwlock_unlock(&rwlock) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
return NULL;
}
printf("스레드[%d] %d원\n", d.count, balance);
if (pthread_rwlock_unlock(&rwlock) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
///////////////////////////////////
while (1) {
if (clock_gettime(CLOCK_MONOTONIC, &end) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
deltaTime = end.tv_sec - start.tv_sec;
//printf("delta %ld\n", deltaTime);
if (deltaTime >= 1) {
break;
}
if (nanosleep(&sleep, NULL) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
}
}
return NULL;
}
void *Update(void *data)
{
int deltaTime = 0;
struct timespec start = { 0, 0 };
struct timespec end = { 0, 0 };
struct timespec sleep = { 0, 010000000 }; //sec:0, nsec:01 => 0.01초
DATA d = *(DATA *)data;
while (1) {
if (clock_gettime(CLOCK_MONOTONIC, &start) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
//////////* WRITE lock *//////////
if (pthread_rwlock_wrlock(&rwlock) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
if (balance >= MAX_BALANCE) {
if (pthread_rwlock_unlock(&rwlock) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
return NULL;
}
balance += d.amount;
if (d.amount > 0) {
printf("스레드[%d] +%d\n", d.count, d.amount);
}
else if (d.amount < 0) {
printf("스레드[%d] %d\n", d.count, d.amount);
}
if (pthread_rwlock_unlock(&rwlock) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
///////////////////////////////////
while (1) {
if (clock_gettime(CLOCK_MONOTONIC, &end) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
deltaTime = end.tv_sec - start.tv_sec;
//printf("delta %ld\n", deltaTime);
if (d.amount < 0 && deltaTime >= 2) {
break;
}
else if (d.amount > 0 && deltaTime >= 1) {
break;
}
if (nanosleep(&sleep, NULL) != 0) {
fprintf(stderr, "errno[%d]", errno);
return NULL;
}
}
}
return NULL;
}