生產者消費者問題,又有界緩沖區問題。兩個進程共享一個一個公共的固定大小的緩沖區。其中一個是生產者,將信息放入緩沖區,另一個是消費者,從緩沖區中取信息。
問題的關鍵在於緩沖區已滿,而此時生產者還想往其中放入一個新的數據的情況。其解決辦法是讓生產者睡眠,待消費者從緩沖區中取出一個或多個數據時再喚醒它,同樣的, 當消費者試圖從緩沖區中取數據而發現緩沖區空時,消費者就睡眠,直到消費者向其中放一些數據后再將其喚醒。
上述方法可以用互斥量解決,程序代碼:
1 #include<sys/types.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 #include<stdio.h> 5 #include<pthread.h> 6 #include<semaphore.h> 7 8 //消費者進程 9 void *thread_consumer(void *ptr); 10 //生產者進程 11 void *thread_producer(void *ptr); 12 13 #define MAX 100000 /*生產者需要生產的數量*/ 14 pthread_mutex_t the_mutex; /*互斥量*/ 15 pthread_cond_t condc,condp;/*生產者和消費者的線程條件變量*/ 16 17 //初始時緩沖區中沒有數據 18 int buffer=0; 19 20 21 int buffer_max=0;/*用於記錄緩沖區最多被用了多少*/ 22 23 #define BUFFER_SIZE 10000 /*緩沖區大小*/ 24 25 /*消費者線程*/ 26 void *thread_consumer(void *arg) 27 { 28 int i; 29 //消費完指定數目 退出線程 30 for(i=0;i<MAX;i++) 31 { 32 //互斥量加鎖,線程同步 33 pthread_mutex_lock(&the_mutex); 34 /*緩沖區空 該進程睡眠 睡眠時pthread_cond_wait函數會對互斥量the_mutex解鎖,這樣生產者線程可以正常工作*/ 35 while(buffer==0) 36 { 37 pthread_cond_wait(&condc,&the_mutex); 38 } 39 //緩沖區非空 此時pthread_cond_wait又會對互斥量再次加鎖 40 --buffer; //消耗一個元素 41 printf("consume an element, buffer=%d\n",buffer); //打印緩沖區中剩余元素 42 //如果緩沖區中元素個數為0 喚醒生產者線程 43 if(buffer==0) 44 { 45 pthread_cond_signal(&condp); 46 } 47 //釋放互斥量 48 pthread_mutex_unlock(&the_mutex); 49 } 50 pthread_exit(NULL); 51 } 52 53 /*生產者線程*/ 54 void *thread_producer(void *ptr) 55 { 56 int i; 57 //消費完指定數目 退出線程 58 for(i=0;i<MAX;i++) 59 { 60 //互斥量加鎖,線程同步 61 pthread_mutex_lock(&the_mutex); 62 //緩沖區滿, 該進程睡眠 睡眠時pthread_cond_wait函數會對互斥量the_mutex解鎖,這樣消費者線程可以正常工作*/ 63 while(buffer==BUFFER_SIZE) 64 { 65 pthread_cond_wait(&condp,&the_mutex); 66 } 67 // 緩沖區未滿時 此時pthread_cond_wait又會對互斥量再次加鎖 68 ++buffer;//生產者增加一個數據 69 /*記錄緩沖區最大的數據個數*/ 70 if(buffer>buffer_max) 71 buffer_max=buffer; 72 printf("produce an element, buffer=%d\n",buffer); 73 //如果緩沖區中元素個數大於0 喚醒消費者線程 74 if(buffer>0) 75 { 76 pthread_cond_signal(&condc); 77 } 78 //釋放互斥量 79 pthread_mutex_unlock(&the_mutex); 80 } 81 } 82 83 int main() 84 { 85 pthread_t pro,con; 86 //初始化互斥量 87 pthread_mutex_init(&the_mutex,0); 88 //初始化線程條件變量 89 pthread_cond_init(&condc,0); 90 pthread_cond_init(&condp,0); 91 //創建生產者、消費者兩個線程 92 pthread_create(&con,NULL,thread_consumer,NULL); 93 pthread_create(&pro,NULL,thread_producer,NULL); 94 //等待兩個線程結束 95 pthread_join(pro,0); 96 pthread_join(con,0); 97 //清除變量 98 pthread_cond_destroy(&condc); 99 pthread_cond_destroy(&condp); 100 pthread_mutex_destroy(&the_mutex); 101 printf("buffer_max=%d",buffer_max); 102 }
在linux下運行時,可以看到兩個線程交替運行,為了看到緩沖區最大能被添加到多少,我把緩沖區大小設置的很大,這樣每次運行程序,打印的緩沖區的最大數都是不一樣的,這跟實際的線程調度有關。
科學家就餐問題:
問題描述:假設有五位哲學家圍坐在一張圓形餐桌旁,做以下兩件事情之一:吃飯,或者思考。吃東西的時候,他們就停止思考,思考的時候也停止吃東西。餐桌中間有一大碗意大利面,每兩個哲學家之間有一只餐叉。因為用一只餐叉很難吃到意大利面,所以假設哲學家必須用兩只餐叉吃東西。他們只能使用自己左右手邊的那兩只餐叉。哲學家就餐問題有時也用米飯和筷子而不是意大利面和餐叉來描述,因為很明顯,吃米飯必須用兩根筷子。用下面的圖描述很現實
問題解決:每個哲學家對應一個線程,程序中定義一個互斥量,對於每個線程進行訪問其他哲學家狀態時(關鍵代碼)用互斥量進行加鎖,這樣也就避免了死鎖的產生,訪問到該哲學家處於飢餓時,同時旁邊兩位科學家並未處於進餐狀態時,他就拿起左右兩邊的叉子進行吃飯,吃飯一段時間后,就放下叉子進行思考,思考一段時間后處於飢餓狀態,重新開始試圖拿起叉子吃飯,代碼如下:
#include<sys/types.h> #include<unistd.h> #include<stdlib.h> #include<stdio.h> #include<pthread.h> #include<semaphore.h> #include<time.h> #define N 5 //哲學家數量 #define LEFT(i) (i+N-1)%N //左手邊哲學家編號 #define RIGHT(i) (i+1)%N //右手邊哲家編號 #define HUNGRY 0 //飢餓 #define THINKING 1 //思考 #define EATING 2 //吃飯 #define U_SECOND 1000000 //1秒對應的微秒數 pthread_mutex_t mutex; //互斥量 int state[N]; //記錄每個哲學家狀態 //每個哲學家的思考時間,吃飯時間,思考開始時間,吃飯開始時間 clock_t thinking_time[N], eating_time[N], start_eating_time[N], start_thinking_time[N]; //線程函數 void *thread_function(void *arg); int main() { pthread_mutex_init(&mutex, NULL); pthread_t a,b,c,d,e; //為每一個哲學家開啟一個線程,傳遞哲學家編號 pthread_create(&a,NULL,thread_function,"0"); pthread_create(&b,NULL,thread_function,"1"); pthread_create(&c,NULL,thread_function,"2"); pthread_create(&d,NULL,thread_function,"3"); pthread_create(&e,NULL,thread_function,"4"); //初始化隨機數種子 srand((unsigned int)(time(NULL))); while(1) { ; } } void *thread_function(void *arg) { char *a = (char *)arg; int num = a[0] - '0'; //根據傳遞參數獲取哲學家編號 int rand_time; while(1) { //關鍵代碼加鎖 pthread_mutex_lock(&mutex); //如果該哲學家處於飢餓 並且 左右兩位哲學家都沒有在吃飯 就拿起叉子吃飯 if(state[num] == HUNGRY && state[LEFT(num)] != EATING && state[RIGHT(num)] != EATING) { state[num] = EATING; start_eating_time[num] = clock(); //記錄開始吃飯時間 eating_time[num] = (rand() % 5 + 5) * U_SECOND; //隨機生成吃飯時間 //輸出狀態 printf("state: %d %d %d %d %d\n",state[0],state[1],state[2],state[3],state[4]); //printf("%d is eating\n",num); } else if(state[num] == EATING) { //吃飯時間已到 ,開始思考 if(clock() - start_eating_time[num] >= eating_time[num]) // { state[num] = THINKING; //printf("%d is thinking\n",num); printf("state: %d %d %d %d %d\n",state[0],state[1],state[2],state[3],state[4]); start_thinking_time[num] = clock(); //記錄開始思考時間 thinking_time[num] = (rand() % 10 + 10) * U_SECOND; //隨機生成思考時間 } } else if(state[num] == THINKING) { //思考一定時間后,哲學家餓了,需要吃飯 if(clock() - start_thinking_time[num] >= thinking_time[num]) { state[num] = HUNGRY; printf("state: %d %d %d %d %d\n",state[0],state[1],state[2],state[3],state[4]); // printf("%d is hungry\n",num); } } pthread_mutex_unlock(&mutex); } }