【實驗目的】
- 掌握並靈活使用線程機制
- 掌握並能夠靈活使用同步互斥機制
- 了解並能夠較靈活地使用IO技術
【實驗要求】
● 基於線程的生產者-消費者的合作問題
– 其中(生產者)從外設獲取數據進行生產
– 另外(消費者)消費后進行輸出,並存儲輸出結果。
●在Linux環境下使用POSIX庫進行設計實現
●鼓勵使用QT進行圖形化顯示
●根據情況決定是否進行答辯
●可以2人一組,但不能超過2人,在報告中必須要有明確分工
【問題描述】
●完成N個生產者和M個消費者線程之間的並發控制,N、M不低於30,數據發送和接收緩沖區尺寸不小於20個(每個產品占據一個)。
●其中生產者線程1、3、5、7、9生產的產品供所有奇數編號的消費者線程消費,只有所有奇數編號的消費者線程都消費后,該產品才能從緩沖區中撤銷。
●其中生產者線程2、4、6、8、10生產的產品所有偶數編號的消費者線程都可消費,任一偶數編號消費者線程消費該消息后,該產品都可從緩沖區中撤銷。
●其中11-20號生產者線程生產的產品僅供對應編號的消費者線程消費。
●其他編號生產者線程生產的產品可由任意的消費者線程消費。
●每個生產線程生產30個消息后結束運行。如果一個消費者線程沒有對應的生產者線程在運行后,也結束運行。所有生產者都停止生產后,如果消費者線程已經沒有 可供消費的產品,則也退出運行。
【小組分工】
問題的分析有兩人共同完成;生產者和第二類,第四類主要由ZX負責。我負責第一、三類消費者的代碼編寫,以及代碼的整理。
【實驗原理】
初始化互斥鎖:
pthread_mutex_init() int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutex_mutexattr *mattr);
如果互斥鎖已初始化,則它會處於未鎖定狀態。互斥鎖可以位於進程之間共享的內存中或者某個進程的專用內存中。
鎖定互斥鎖
使用pthread_mutex_lock() 可以鎖定mutex 所指向的互斥鎖。
pthread_mutex_lock 語法
int pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_lock 返回值
pthread_mutex_lock() 在成功完成之后會返回零。其他任何返回值都表示出現了錯誤。
解除鎖定互斥鎖
使用pthread_mutex_unlock() 可以解除鎖定mutex 所指向的互斥鎖。
銷毀條件變量
使用cond_destroy() 可以銷毀與cv 所指向的條件變量相關聯的狀態。用來存儲該條件變量的空間不會釋放。
信號量
Sem_init()/*初始化信號量*/ Sem_wait()/*wait,p原語*/ Sem_post()/*signal v原語*/
【實驗分析】
首先應該清楚生產者之間是互斥的關系,消費者之間也是互斥的關系。而生產者與消費者之間是同步的關系。
本問題的難點在於不同編號的消費者與不同編號的消費者之間復雜的業務邏輯關系。只要搞清楚了各個生產者與消費者之間的業務邏輯關系,然后再把同步互斥問題解決,問題就基本得以解決了。
為了解決對產品緩沖區的讀寫互斥問題,必須定義一個互斥量。在本方案中定義了mutex_buffer。不管是生產者還是消費者,只要是要對產品緩沖區進行讀寫操作,都要對其上鎖以實現互斥。這樣,在任一時刻,只有一個線程可以對緩沖區進行操作。
為了解決生產者與消費者之間的同步問題,本方案中采用了信號量的同步機制。定義了兩個信號量buffer_full、buffer_empty。Buffer_full初始化為0,用來表示可以消費的產品數量,buffer_empty初始化為BUFFER_SIZE(宏定義,表示產品緩沖區大小)。當buffer_full大於0的時候,表示有產品可以消費,對應的消費者可以對該產品進行消費;當buffer_full小於或等於零的時候表示沒有可供消費的產品,這時候消費者發生阻塞,等待生產者生產產品。同理,當buffer_empty大於零的時候表示有可以供生產者生產產品的緩沖區空間,生產者可以生產產品;當buffer_empty等於零的時候,表示緩沖區是滿的,生產者這時候會等待消費者將產品消費掉。
生產者與消費者之間業務邏輯的處理。因為在問題描述中,不同編號的生產者生產的產品所供應的消費者對象不同,為了解決這個問題,我在讓生產者生產產品的時候把自己的編號寫入到了產品里面。這樣就必須在產品的結構體中定義一個變量用來記錄該產品的生產商(在本方案中使用了int 型的變量num來表示生產商的)。定義這個變量的好處就是生產者不需要分類了。生產者的唯一工作就是生產產品,並將自己的編號寫入到產品上。生產的產品該有誰來消費需要消費者自己來判斷。這樣就需要對消費者進行分類。這里把消費者分為了4類:是奇數號,但編號不屬於11-20的消費者;是偶數號,但編號不屬於11-20的消費者;是奇數號,且編號屬於11-20的消費者;是偶數號,且編號屬於11-20的消費者。對消費者如此分類與問題的業務邏輯有關。對於第一類消費者,1,3,5,7,9,21-30號生產者生產的產品它都可以消費。對於第二類消費者,2,4,6,8,10,21-30號生產者生產的產品它可以消費。對於第三類消費者,它不僅可以消費第一類消費者可以消費的產品,而且它還可以消費與自己的編號相同的產品。對於第四類消費者,它不僅可以消費第二類消費者可以消費的產品,而且它還可以消費與自己編號相同的產品。
產品的撤銷問題。對於1,3,5,7,9號線程生產的產品,必須所有的奇數號的消費者都消費以后才能從緩沖區撤銷(所謂從緩沖區撤銷,就是將read指針向前移動一個單位,即read++)。這樣在第一,三類緩沖區中需要進行一次判斷,如果該產品是1,3,5,7,9號產品,那么該產品被自己消費后不能從緩沖區撤銷。那么,消費者該怎樣判斷該產品是否已經被所有的奇數號消費者消費過了呢?我的解決辦法是在在產品的結構體中定義了一個位標識符。該位標識符是一個無符號的三十二位的整形變量bitsign。產品生產出來以后該變量要被初始化為0。消費者對1,3,5,7,9號產品進行消費的時候先判斷一下bitsign的1,3,5,7,9,···,29號位置上是否全都標記為1了。如果是,那么就說明已經被所有的奇數號消費者消費過,直接將其從緩沖區撤銷就好了。如果不是,再判斷在自己編號所在的位置是否已經置一,如果沒有就說明自己沒有消費過。這樣就將產品的內容讀出來,然后在bitsign相應的位置上寫1。但是這時候不能將產品從緩沖區撤銷。對於第二、四類消費者,因為業務邏輯要求只要有偶數消費者消費過該產品,產品就可以從緩沖區撤銷這樣就不需要bitsign了。只要讀完產品然后從緩沖區撤銷就可以了。
線程的撤銷問題。生產者的撤銷是很容易的,只要生產者線程生產完了三十個產品就可以結束了。消費者線程結束比較棘手。我們有兩個解決方案。方案一:設置一個無符號的長整形變量dead,在生產者生產完30個產品,即將結束之前要在與自己的編號相對應的dead標志位上置位為1,表示該線程結束。每個消費者線程在讀取緩沖區之前的數據之前,先檢查dead的標記位如果對應的標記位為1,那么就表明這個消費者線程需要結束了。該方案缺陷,消費者無法保證已經把產品緩沖區的產品已經全部讀取完畢。方案二:換一個思路,讓消費者在進入臨界區之前在dead對應的位置上置位1表明消費者有可能發生阻塞,當消費者已經進入臨界區了,再將dead標記位對應的位置清零。再創建一個線程dead checker,如果每隔一段時間就對dead進行一次檢測,如果檢測到相應的位置上都已置位為1,那么就表明消費者需要結束了。Dead checker已經將所有的消費者線程撤銷了,就把自己結束。由於時間有限,操作系統還沒有復習,之前也沒有想好,就用了方案一,后來測試以后不對,就想了方案二,但是還沒有代碼實現,也不知道對不對。有機會的話可以再實現。
【代碼分析】
由於代碼繁雜,這里只對關鍵代碼分析。所有代碼請看隨設計報告附帶的代碼。
struct Product { int num;/*用於表示是哪一個生產者生產的*/ int data;/*生產的數據*/ unsigned int bitsign;/*位標識符,編號為num的消費者消費該產品后,在相應的位置寫如1,標記為已經讀過該產品*/ };
上面是定義的產品的結構體。
struct Product product[BUFFER_SIZE];
上面是定義的產品的緩沖區。
int write_pos;/*寫指針,生產者每生產一個產品指針加一*/
int read_pos;/*讀指針,每當有一個產品撤銷,指針+1*/
write_pos 是消費者當前需要消費的產品的指針。
pthread_mutex_t mutex_buffer;
pthread_mutex_t mutex_dead;
這是定義的互斥量,mutex_buffer是對產品緩沖區上鎖時使用的。
Mutex_dead是標記線程結束時使用的。
sem_t buffer_empty;
sem_t buffer_full;
這是定義的兩個信號量。Buffer_empty代表了空的緩沖區個數。Buffer_full代表了產品的個數。這兩個信號量用來控制生產者和消費者線程之間的同步。
void put(int number);是生產者生產產品的函數,number是該生產者的線程號。
【結果分析】
第一類消費者:
截圖如下:
第一類消費者
從上圖我們可以看到,一號生產者生產的產品已經被所有的奇數好消費者消費過,並且最后一個消費者已經將這個產品從緩沖區撤銷了。
對於第二類消費者:
第二類消費者
我們從截圖看到,二號生產者生產的產品被22號消費者消費過了后,將產品從緩沖區撤銷了。接下來三號生產者生產了產品,被15號消費者消費過了。
第三四類消費者:
第三、四類消費者可以消費對應編號的產品。而且對應編號的產品只能被對應得消費者消費。截圖如下:
第三、四類消費者
【性能分析】
本方案存在效率低下的問題。分析如下:
因為產品是按照一定的順序被消費的,例如生產者在緩沖區生產了一號、二號產品,但是read_pos指針現在只有所有的奇數號消費者消費過一號產品后才能指向第二號產品。即產品是按照FIFO的順序被訪問的。這樣就使得系統的效率大打折扣。這是系統應該改進的一個方面。
【存在問題】
消費者線程無法結束。這是本方案有待解決的一個問題。
【解決辦法】
打算試一下前面提到的方案二,讓消費者在進入臨界區之前在dead對應的位置上置位1表明消費者有可能發生阻塞,當消費者已經進入臨界區了,再將dead標記位對應的位置清零。再創建一個線程dead checker,如果每隔一段時間就對dead進行一次檢測,如果檢測到相應的位置上都已置位為1,那么就表明消費者需要結束了。Dead checker已經將所有的消費者線程撤銷了,就把自己結束。
時間有限,還要復習操作系統,本方案留在寒假解決。
【領悟感想】
在本次課程設計中,剛開始研究本題目時感覺無從下手。經過認真的研究才漸漸有了眉目。我們的感想是,在寫代碼之前一定要先想好解決各種問題的方案。產品的結構體需要定義那些變量?線程號應該怎么產生?要對生產者分類還是對消費者分類?怎樣分類?這些都是很關鍵的問題。
在本次課程設計中,我收獲了很多知識和快樂。對互斥鎖和信號量的理解加深了。更重要的是我獲得了快樂,我認為快樂大體分為兩類:消費型的快樂和創造型的快樂。消費型的快樂,比如吃一次大餐,獲得了更多的社會資源···,這些可以使你快樂,但這些不能讓你得到真正的滿足,只能讓你為了獲得更多的快樂而變得更加貪婪。而創造型的快樂卻更能給自己帶來滿足感和成就感,而且這種快樂是持續的,有更大的意義。
【參考文獻】
《Operating System Concept》 by Abraham
《Linux 多線程編程手冊》 SUN microsystem
《Linux 高級程序設計》 楊宗德 等
【附帶源碼】

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 #include <string.h> 5 #include <math.h> 6 #include <semaphore.h> 7 8 #define BUFFER_SIZE 12 9 #define THREAD_NUM 30 10 11 struct Product 12 { 13 int num;/*用於表示是哪一個生產者生產的*/ 14 int data;/*生產的數據*/ 15 unsigned int bitsign;/*位標識符,編號為num的消費者消費該產品后,在相應的位置寫如1,標記為已經讀過該產品*/ 16 }; 17 18 struct Product product[BUFFER_SIZE]; 19 FILE *fp; 20 int write_pos;/*寫指針,生產者每生產一個產品指針加一*/ 21 int read_pos;/*讀指針,每當有一個產品撤銷,指針+1*/ 22 int producer_num;/*生產者線程的編號*/ 23 int consumer_num;/*消費者線程的編號*/ 24 unsigned int dead;/*生產者線程撤銷標記位,生產者進程結束時在相應的位置上置一以表示死亡*/ 25 pthread_t producer_id[THREAD_NUM]; 26 pthread_t consumer_id[THREAD_NUM]; 27 28 pthread_mutex_t mutex_buffer; 29 pthread_mutex_t mutex_dead; 30 pthread_mutex_t mutex_producer_num; 31 pthread_mutex_t mutex_consumer_num; 32 pthread_cond_t notempty; 33 pthread_cond_t notfull; 34 sem_t buffer_empty; 35 sem_t buffer_full; 36 37 38 void init(); 39 void *produce(); 40 void *consume(); 41 void create_producer(); 42 void create_consumer(); 43 void join(); 44 void put(int number); 45 void get(int number); 46 47 int main() 48 { 49 init(); 50 create_producer(); 51 create_consumer(); 52 join(); 53 //pthread_t ta,tb; 54 //pthread_create(&ta,NULL,produce,0); 55 //pthread_create(&tb,NULL,consume,0); 56 //pthread_join(ta,NULL); 57 //pthread_join(tb,NULL); 58 return 0; 59 } 60 61 void init() 62 { 63 dead = 0; 64 producer_num = 0; 65 consumer_num = 0; 66 write_pos = 0; 67 read_pos = 0; 68 pthread_mutex_init(&mutex_buffer,NULL); 69 pthread_mutex_init(&mutex_dead,NULL); 70 sem_init(&buffer_empty,0,BUFFER_SIZE); 71 sem_init(&buffer_full,0,0); 72 pthread_cond_init(¬empty,NULL); 73 pthread_cond_init(¬full,NULL); 74 } 75 76 void *produce() 77 { 78 /*為每個生產者編號*/ 79 int number; 80 pthread_mutex_lock(&mutex_producer_num); 81 producer_num ++; 82 number = producer_num; 83 pthread_mutex_unlock(&mutex_producer_num); 84 put(number); 85 } 86 87 void *consume() 88 { 89 /*為每個消費者編號*/ 90 int number; 91 pthread_mutex_lock(&mutex_consumer_num); 92 consumer_num++; 93 number = consumer_num; 94 pthread_mutex_unlock(&mutex_consumer_num); 95 get(number); 96 } 97 98 void create_producer() 99 { 100 //sleep(1); 101 int i; 102 for(i = 0; i<THREAD_NUM; i++) 103 { 104 pthread_create(&(producer_id[i]), NULL, produce, 0); 105 } 106 } 107 void join() 108 { 109 int i; 110 for(i=0;i<THREAD_NUM;i++) 111 { 112 pthread_join(producer_id[i],NULL); 113 pthread_join(consumer_id[i],NULL); 114 } 115 } 116 void create_consumer() 117 { 118 int i; 119 for(i = 0; i<THREAD_NUM; i++) 120 { 121 pthread_create( (consumer_id+i), NULL, consume, 0 ); 122 } 123 } 124 void put(int number) 125 { 126 int i; 127 for(i=0;i<30;i++) 128 { 129 sem_wait(&buffer_empty); 130 pthread_mutex_lock(&mutex_buffer); 131 (product+write_pos)->num = number; 132 (product+write_pos)->data = rand()%100; 133 (product+write_pos)->bitsign = 0; 134 write_pos++; 135 if(write_pos>=BUFFER_SIZE) 136 { 137 write_pos = 0; 138 } 139 pthread_mutex_unlock(&mutex_buffer); 140 sem_post(&buffer_full); 141 } 142 //sleep(1); 143 pthread_mutex_lock(&mutex_dead); 144 dead = dead | (1<<number); 145 pthread_mutex_unlock(&mutex_dead); 146 } 147 148 void get(int number) 149 { 150 if((number%2==1) && (number<11 || number>20) )/*第一類消費者*/ 151 { 152 while(1) 153 { 154 sleep(1); 155 pthread_mutex_lock(&mutex_dead); 156 if((dead & 0x7fe002aa)==0x7fe002aa)/*對應的1,3,5,7,9,21-30號生產者已經結束運行*/ 157 { 158 printf("consumer number %d cancel\n",number); 159 pthread_mutex_unlock(&mutex_dead); 160 pthread_cancel(pthread_self()); 161 } 162 pthread_mutex_unlock(&mutex_dead); 163 sem_wait(&buffer_full); 164 pthread_mutex_lock(&mutex_buffer); 165 if( (((product+read_pos)->num)%2==1 && ((product+read_pos)->num)<10) 166 ||((product+read_pos)->num)>20 )/*是自己需要的產品*/ 167 { 168 if(((product+read_pos)->num)>20) 169 { 170 printf("我是%d號消費者,我消費的產品:\n",number); 171 printf("產品號:%d, 產品內容%d\n",(product+read_pos)->num,(product+read_pos)->data); 172 read_pos++;/*產品從緩沖區撤銷*/ 173 if(read_pos >= BUFFER_SIZE) 174 { 175 read_pos = 0; 176 } 177 pthread_mutex_unlock(&mutex_buffer); 178 sem_post(&buffer_empty); 179 } 180 else if( (((product+read_pos)->bitsign) & 0x2aaaaaaa)== 0x2aaaaaaa )/*奇數號消費者都消費過*/ 181 { 182 printf("奇數號消費者都消費過,產品撤銷\n"); 183 read_pos++;/*產品從緩沖區撤銷*/ 184 if(read_pos >= BUFFER_SIZE) 185 { 186 read_pos = 0; 187 } 188 pthread_mutex_unlock(&mutex_buffer); 189 sem_post(&buffer_empty); 190 } 191 else 192 { 193 if( (((product+read_pos)->bitsign) & (1<<number))==(1<<number) )/*自己消費過*/ 194 { 195 pthread_mutex_unlock(&mutex_buffer); 196 sem_post(&buffer_full); 197 } 198 else 199 { 200 printf("我是%d號消費者,我消費的產品:\n",number); 201 printf("產品號:%d, 產品內容%d\n",(product+read_pos)->num,(product+read_pos)->data); 202 ((product+read_pos)->bitsign) = ((product+read_pos)->bitsign)|(1<<number); 203 pthread_mutex_unlock(&mutex_buffer); 204 sem_post(&buffer_full); 205 } 206 } 207 } 208 else 209 { 210 pthread_mutex_unlock(&mutex_buffer); 211 sem_post(&buffer_full); 212 } 213 } 214 } 215 else if( (number%2==0)&&(number<11 || number>20) )/*第二類消費者*/ 216 { 217 while(1) 218 { 219 sleep(1); 220 pthread_mutex_lock(&mutex_dead); 221 if((dead & 0x7fe00554)==0x7fe00554 ) 222 { 223 pthread_mutex_unlock(&mutex_dead); 224 printf("consumer number %d cancel\n",number); 225 pthread_cancel(pthread_self()); 226 } 227 pthread_mutex_unlock(&mutex_dead); 228 sem_wait(&buffer_full); 229 pthread_mutex_lock(&mutex_buffer); 230 if( ((((product+read_pos)->num)%2==0)&&((product+read_pos)->num)<11) 231 || ((product+read_pos)->num>20) )/*產品編號是偶數,並且小於11;或者產品編號大於20*/ 232 { 233 printf("我是%d號消費者,我消費的產品內容:\n",number); 234 printf("產品號:%d, 產品內容%d\n",(product+read_pos)->num,(product+read_pos)->data); 235 read_pos++; 236 if(read_pos>=BUFFER_SIZE) 237 { 238 read_pos = 0; 239 } 240 pthread_mutex_unlock(&mutex_buffer); 241 sem_post(&buffer_empty); 242 } 243 else 244 { 245 pthread_mutex_unlock(&mutex_buffer); 246 sem_post(&buffer_full); 247 } 248 } 249 } 250 else if( (number%2==1)&&(number>10 && number<21) )/*第三類消費者*/ 251 { 252 while(1) 253 { 254 sleep(1); 255 pthread_mutex_lock(&mutex_dead); 256 if( ((dead & 0x7fe002aa)==0x7fe002aa)&&( (dead & (1<<number))==(1<<number)) ) 257 /*對應的1,3,5,7,9,21-30號生產者,和只給自己生產產品的已經結束運行*/ 258 { 259 pthread_mutex_unlock(&mutex_dead); 260 printf("consumer number %d cancel\n",number); 261 pthread_cancel(pthread_self()); 262 } 263 pthread_mutex_unlock(&mutex_dead); 264 sem_wait(&buffer_full); 265 pthread_mutex_lock(&mutex_buffer); 266 if( (((product+read_pos)->num)%2==1 && ((product+read_pos)->num)<10) 267 ||((product+read_pos)->num)>20 || ((product+read_pos)->num)==number )/*是自己需要的產品*/ 268 { 269 if(((product+read_pos)->num)==number) 270 { 271 printf("我是%d號消費者,我消費的產品:\n",number); 272 printf("產品號:%d, 產品內容%d\n",(product+read_pos)->num,(product+read_pos)->data); 273 read_pos++;/*產品從緩沖區撤銷*/ 274 if(read_pos >= BUFFER_SIZE) 275 { 276 read_pos = 0; 277 } 278 pthread_mutex_unlock(&mutex_buffer); 279 sem_post(&buffer_empty); 280 } 281 else if(((product+read_pos)->num)>20) 282 { 283 printf("我是%d號消費者,我消費的產品:\n",number); 284 printf("產品號:%d, 產品內容%d\n",(product+read_pos)->num,(product+read_pos)->data); 285 read_pos++;/*產品從緩沖區撤銷*/ 286 if(read_pos >= BUFFER_SIZE) 287 { 288 read_pos = 0; 289 } 290 pthread_mutex_unlock(&mutex_buffer); 291 sem_post(&buffer_empty); 292 } 293 else if( (((product+read_pos)->bitsign) & 0x2aaaaaaa)== 0x2aaaaaaa )/*奇數號消費者都消費過*/ 294 { 295 printf("奇數號消費者都消費過,產品撤銷\n"); 296 read_pos++; 297 if(read_pos >= BUFFER_SIZE) 298 { 299 read_pos = 0; 300 } 301 pthread_mutex_unlock(&mutex_buffer); 302 sem_post(&buffer_empty); 303 } 304 else 305 { 306 if( (((product+read_pos)->bitsign) & (1<<number))==(1<<number) )/*自己消費過*/ 307 { 308 printf("我是%d號消費者,我已經消費過該產品\n",number); 309 pthread_mutex_unlock(&mutex_buffer); 310 sem_post(&buffer_full); 311 } 312 else 313 { 314 printf("我是%d號消費者,我消費的產品:\n",number); 315 printf("產品號:%d, 產品內容%d\n",(product+read_pos)->num,(product+read_pos)->data); 316 ((product+read_pos)->bitsign) = ((product+read_pos)->bitsign)|(1<<number); 317 pthread_mutex_unlock(&mutex_buffer); 318 sem_post(&buffer_full); 319 } 320 } 321 } 322 else 323 { 324 pthread_mutex_unlock(&mutex_buffer); 325 sem_post(&buffer_full); 326 } 327 } 328 } 329 else/*第四類消費者*/ 330 { 331 while(1) 332 { 333 sleep(1); 334 pthread_mutex_lock(&mutex_dead); 335 if((dead & 0x7fe00554)==0x7fe00554 ) 336 { 337 pthread_mutex_unlock(&mutex_dead); 338 printf("consumer number %d cancel\n",number); 339 pthread_cancel(pthread_self()); 340 } 341 pthread_mutex_unlock(&mutex_dead); 342 sem_wait(&buffer_full); 343 pthread_mutex_lock(&mutex_buffer); 344 if( ((((product+read_pos)->num)%2==0)&&((product+read_pos)->num)<11) 345 || ((product+read_pos)->num>20) || (product+read_pos)->num == number ) 346 /*產品編號是偶數,並且小於11;或者產品編號大於20;或者產品號與自己的號碼相同*/ 347 { 348 printf("我是%d號消費者,我消費的產品內容:\n",number); 349 printf("產品號:%d, 產品內容%d\n",(product+read_pos)->num,(product+read_pos)->data); 350 read_pos++; 351 if(read_pos>=BUFFER_SIZE) 352 { 353 read_pos = 0; 354 } 355 pthread_mutex_unlock(&mutex_buffer); 356 sem_post(&buffer_empty); 357 } 358 else 359 { 360 pthread_mutex_unlock(&mutex_buffer); 361 sem_post(&buffer_full); 362 } 363 } 364 } 365 }