Producer/Consumer 문제를 해결하는 프로그램을 작성 하라.
- 자료를 저장할 큐를 만들고 Pthread를 사용하여 Producer/Consumer thread를 생성한다. Producer thread는 큐의 자료를 1로 계속 채우고, Consumer thread는 큐의 자료를 소모하여 0으로 만든다. 이때, Consumer thread는 큐의 가장 오래된 자료부터 소모하고, Producer thread는 큐의 가장 최근에 자료를 생성했던 다음 빈자리부터 자료를 채운다. (최초의 자료 생성시 큐의 가장 왼쪽부터 생성하고, 큐의 가장 오른쪽에 도달 시 큐의 가장 왼쪽으로 이동한다.) Consumer thread가 자료를 소모하거나 Producer thread가 자료를 생성하는 것을 각각 1번의 Request라고 취급한다. 프로그램을 시작할 때, 큐의 사이즈, 실행할 Request의 수, Producer thread의 수, Consumer thread의 수를 인자로 입력 받을 수 있고 입력방법은 다음과 같다. 그리고 입력 받은 만큼의 수만큼 Request가 일어났을 때, 프로그램을 종료한다.
$hw02 -q “큐의 사이즈” -r “Request의 수” -p “Producer thread의 수” –c “Consumer thread의 수”
예)$hw02 –q 100 –r 1000 –p 2 –c 5
프로그램 실행 시 위와 같이 입력 받았을 때, 100개의 자료를 저장할 수 있는 큐를 만들고, 2개의 Producer thread가 자료를 생성, 5개의 Consumer thread가 자료를 소모하고, 총 1000개의 Request가 수행 되었을 때 프로그램을 종료한다.
인자들 중에 제한범위를 벗어난 것이 있으면 오류 메시지를 출력하고 바로 프로그램을 종료하고, 만약 프로그램을 실행 시 인자를 생략한다면 default값으로 프로그램을 실행한다.
|
제한범위 |
디폴트 값 |
-q |
1~200 |
100 |
-r |
1`~50000 |
5000 |
-p |
1~10 |
3 |
-c |
1~10 |
3 |
프로그램이 실행되면 Request가 수행 될 때마다 Request의 번호와 큐의 내용을 한 줄로 출력한다.
출력의 예시는 다음과 같다.
Request를 모두 수행하고 난 후에 아래와 같이 각thread들이 수행한 request 수를 출력하고 프로그램을 종료한다.
과제 1 Mutex를 이용한 동기화 – 위의 Producer/Consumer 문제를 mutex를 이용하여 동기화 시킨다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | #include <stdio.h> #include <string.h> #include <pthread.h> #include <stdlib.h> /* 프로그램 작성자 : americano@korea.ac.kr 개발 날짜 : 2012-11-16 */ int queue[200]; // MAX SIZE = 200 int box = 0; //소비, 생산하는 물건 int r_size = 5000; //request 의 기본 크기는 5000 int q_size = 100; //큐의 기본 크기는 100; int request = 0; //request를 실행한 횟수 int produce = 0; //생산한 횟수 int consume = 0; //소비한 횟수 int produce_num[10]; //생산 쓰레드 번호 배열 int consume_num[10]; //소비 쓰레드 번호 배열 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int set( int what, int id){ int i = 0; pthread_mutex_lock(&mutex); if (request>=r_size){ //명령어를 모두 실행하면 쓰레드종료 pthread_mutex_unlock(&mutex); return -1; } if (what == 1){ if (box>=q_size){ //더이상 생산할 수 없으면 함수종료 pthread_mutex_unlock(&mutex); return 0; } queue[produce%q_size] = 1; produce++; box++; request++; produce_num[id] += 1; } else { if (box<=0){ //더이상 소비 할수 없으면 함수 종료 pthread_mutex_unlock(&mutex); return 0; } queue[consume%q_size] = 0; consume++; box--; request++; consume_num[id] += 1; } //결과 출력 printf ( "%6d " , request); for (i = 0; i<q_size; i++) printf ( "%d" ,queue[i]); printf ( "\n" ); pthread_mutex_unlock(&mutex); return 0; } //생산자 쓰레드 void *produce_thread( void *tid){ int id = *(( int *)tid); int i; while (1){ if (set(1,id)==-1) pthread_exit(NULL); } } //소비자 쓰레드 void *consume_thread( void *tid){ int id = *(( int *)tid); int i; while (1){ if (set(0,id)==-1) pthread_exit(NULL); } } int main( int argc, char * argv[]){ int i; char * q_str = "-q" ; char * r_str = "-r" ; char * p_str = "-p" ; int p_size = 3; char * c_str = "-c" ; int c_size = 3; int status = 0; int p_num[10]; int c_num[10]; pthread_t thread_p[10]; pthread_t thread_c[10]; //init setting for (i = 0; i<argc; i++){ if ( strcmp (argv[i],q_str)==0) q_size = atoi (argv[i+1]); if ( strcmp (argv[i],r_str)==0) r_size = atoi (argv[i+1]); if ( strcmp (argv[i],p_str)==0) p_size = atoi (argv[i+1]); if ( strcmp (argv[i],c_str)==0) c_size = atoi (argv[i+1]); } if (q_size<1 || q_size>200){ printf ( "q_size is not correct\n" ); return -1; } if (r_size<1 || r_size>50000){ printf ( "r_size is not correct\n" ); return -1; } if (p_size<1 || p_size>10){ printf ( "p_size is not correct\n" ); return -1; } if (c_size<1 || c_size>10){ printf ( "c_size is not correct\n" ); return -1; } // 쓰레드 번호를 위해 숫자 초기화 작업 for (i = 0; i<p_size; i++) p_num[i] = i; for (i = 0; i<c_size; i++) c_num[i] = i; //생산자 쓰레드 생성 for (i = 0; i<p_size; i++){ status = pthread_create(&thread_p[i], NULL, produce_thread,( void *) (p_num+i)); if (status != 0) printf ( "produce : pthread_creat returned error code : %d\n" ,status); } //소비자 쓰레드 생성 for (i = 0; i<c_size; i++){ status = pthread_create(&thread_c[i], NULL, consume_thread, ( void *) (c_num+i)); if (status != 0) printf ( "consume : pthread_creat returned error code : %d\n" ,status); } //생산자가 모두 끝날 때 까지 기다림 for (i = 0; i<p_size; i++){ pthread_join(thread_p[i], ( void **)&status); } //소비자가 모두 끝날 때 까지 기다림 for (i = 0; i<c_size; i++){ pthread_join(thread_c[i], ( void **)&status); } // 생산자 쓰레드의 명령어 실행 횟수 출력 for (i = 0; i<p_size; i++){ printf ( "producer %d:%d\n" ,i,produce_num[i]); } printf ( "\n" ); //소비자 쓰레드의 명령어 실행 횟수 출력 for (i = 0; i<c_size; i++){ printf ( "consumer %d:%d\n" ,i,consume_num[i]); } return 0; } |
과제 2.Semaphore를 이용한 동기화 - 위의 Producer/Consumer 문제를 Semaphore를 이용하여 동기화 시킨다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | #include <stdio.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <semaphore.h> /* 프로그램 작성자 : americano@korea.ac.kr 개발 날짜 : 2012-11-16 */ int queue[200]; // MAX SIZE = 200 int box = 0; // 생산, 소비할 매개체 int r_size = 5000; //명령어의 기본크기는 5000 int q_size = 100; //큐의 기본크기는 100 int request = 0; int produce = 0; int consume = 0; int produce_num[10]; //생산자쓰레드의 번호를 저장할 배열 int consume_num[10]; //소비자쓰레드의 번호를 저장할 배열 //세마포어변수들 sem_t full; sem_t empty; sem_t mutex; int set( int what, int id){ int i = 0; if (what == 1){ // 생산자 쓰레드 box++; sem_wait(&empty); sem_wait(&mutex); if (request>=r_size){ //명령어가 모두 실행되면 종료 sem_post(&mutex); sem_post(&full); return -1; } queue[produce%q_size] = 1; produce++; request++; produce_num[id] += 1; printf ( "%6d " , request); //결과출력 for (i = 0; i<q_size; i++) printf ( "%d" ,queue[i]); printf ( "\n" ); sem_post(&mutex); sem_post(&full); return 0; } else { // 소비자 쓰레드 sem_wait(&full); sem_wait(&mutex); //명령어가 모두 실행되면 쓰레드 종료 if (request>= r_size){ sem_post(&mutex); sem_post(&empty); return -1; } queue[consume%q_size] = 0; consume++; request++; consume_num[id] += 1; printf ( "%6d " , request); // 결과출력 for (i = 0; i<q_size; i++) printf ( "%d" ,queue[i]); printf ( "\n" ); sem_post(&mutex); sem_post(&empty); box--; return 0; } } void *produce_thread( void *tid){ // 생산자 쓰레드 int id = *(( int *)tid); int i; while (1){ if (set(1,id)==-1) pthread_exit(NULL); } } void *consume_thread( void *tid){ int id = *(( int *)tid); //소비자 쓰레드 int i; while (1){ if (set(0,id)==-1) pthread_exit(NULL); } } int main( int argc, char * argv[]){ int i; int state; char * q_str = "-q" ; char * r_str = "-r" ; char * p_str = "-p" ; int p_size = 3; char * c_str = "-c" ; int c_size = 3; int status = 0; int p_num[10]; //쓰레드의 번호를 저장할 배열 int c_num[10]; pthread_t thread_p[10]; pthread_t thread_c[10]; //init setting for (i = 0; i<argc; i++){ if ( strcmp (argv[i],q_str)==0) q_size = atoi (argv[i+1]); if ( strcmp (argv[i],r_str)==0) r_size = atoi (argv[i+1]); if ( strcmp (argv[i],p_str)==0) p_size = atoi (argv[i+1]); if ( strcmp (argv[i],c_str)==0) c_size = atoi (argv[i+1]); } if (q_size<1 || q_size>200){ printf ( "q_size is not correct\n" ); return -1; } if (r_size<1 || r_size>50000){ printf ( "r_size is not correct\n" ); return -1; } if (p_size<1 || p_size>10){ printf ( "p_size is not correct\n" ); return -1; } if (c_size<1 || c_size>10){ printf ( "c_size is not correct\n" ); return -1; } for (i = 0; i<p_size; i++) p_num[i] = i; for (i = 0; i<c_size; i++) c_num[i] = i; if (sem_init(&full,0,0)!=0){ printf ( "sem_init Error\n" ); return 0; } if (sem_init(&empty,0,q_size)!=0){ printf ( "sem_init Error\n" ); return 0; } if (sem_init(&mutex,0,1)!=0){ printf ( "sem_init Error\n" ); return 0; } //생산자 쓰레드 생성 for (i = 0; i<p_size; i++){ status = pthread_create(&thread_p[i], NULL, produce_thread,( void *) (p_num+i)); if (status != 0) printf ( "produce : pthread_creat returned error code : %d\n" ,status); } //소비자 쓰레드 생성 for (i = 0; i<c_size; i++){ status = pthread_create(&thread_c[i], NULL, consume_thread, ( void *) (c_num+i)); if (status != 0) printf ( "consume : pthread_creat returned error code : %d\n" ,status); } //쓰레드가 끝날때까지 기다림 for (i = 0; i<p_size; i++){ pthread_join(thread_p[i], ( void **)&status); } for (i = 0; i<c_size; i++){ pthread_join(thread_c[i], ( void **)&status); } //생산자가 모두 끝나면 결과를 출력 for (i = 0; i<p_size; i++){ printf ( "producer %d:%d\n" ,i,produce_num[i]); } printf ( "\n" ); //소비자가 모두 끝나면 결과를 출력 for (i = 0; i<c_size; i++){ printf ( "consumer %d:%d\n" ,i,consume_num[i]); } sem_destroy(&full); sem_destroy(&empty); sem_destroy(&mutex); return 0; } |
Appendix
*자세한 내용은 man 페이지를 참조
1) int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
2) int pthread_join(pthread_t thread, void **retval);
3)
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
4) int sem_init(sem_t *sem, int pshared, unsigned int value);
5) int sem_destroy(sem_t *sem);
6) int sem_wait(sem_t *sem);
7) int sem_post(sem_t *sem);
*기타 참고사항
Pthread API를 사용하는 프로그램은 컴파일시 –lpthread 옵션을 사용하여 libpthread 라이브러리를 링크해야 오류가 나지 않는다.
사용 예) $gcc –o hw02 hw02.c -lpthread
'대학교시절' 카테고리의 다른 글
알고리즘 과제 #1 (이진 트리 높이 구하기) (0) | 2015.09.28 |
---|---|
운영체제 과제 #3 (mmap() 시스템콜 성능) (0) | 2015.09.28 |
운영체제 과제 #1 (fork 와 exec 사용하기) (2) | 2015.09.27 |
PCM(Phase_Change Memory) (0) | 2015.09.27 |
대학교 C언어 과제 모음 (0) | 2015.09.27 |