4-8 동기화 (과제)

do·2022년 5월 17일
0

API

목록 보기
35/42

문제. 은행 잔고 프로그램을 멀티 쓰레드로 작성하시오.

  • 은행 잔고는 전역 변수로 선언
  • 은행 잔고를 조회하는 쓰레드는 3개 존재하고, 각 쓰레드는 은행 잔고를 1초에 한번씩 조회하여 출력하도록 쓰레드 구현
  • 은행 잔고를 업데이트하는 쓰레드는 2개 존재하고, 1개 쓰레드는 은행 잔고를 1초에 1,000원씩 증가, 1개 쓰레드는 은행 잔고를 2초에 500원씩 감소하도록 구현
  • 메인 쓰레드는 은행 잔고가 10,000원 이상이 되면 모든 쓰레드를 종료하고 프로그램 종료하도록 함
  • mutex lock 사용하는 버전과 rwlock 사용하는 버전 2개를 작성

출력. 은행 잔고를 출력한다.
입력 예제. my_balance
출력 예제.
0원
1000원
1000원
1000원
500원
1500원

1. mutex lock 버전 (my_balance_mutex.c)

/* 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;
}

2. rwlock 버전 (my_balance_rwlock.c)

/* 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;
}

0개의 댓글