1.記錄型信號量:為了解決整形信號量讓權等待的問題,添加一個阻塞隊列,記錄型信號量完全符合進程同步准則
(注意阻塞是進程主動的),當進程資源不夠時,進程/線程進入阻塞隊列
程序計數器定位在wait之后:這句話的意思是,記錄型信號量的p操作,總是先預先分配資源,當進程/線程資源滿足時,從阻塞隊列進入就緒隊列調度
執行時,不需要在重新分配資源了,因為提前預先分配了.當進程出來直接運行不用在分配資源了
注意:這里wait和signal中參數不是int類型的,是semaphore類型的,我也不知道,為啥都寫成int類型的,教科書上都這樣寫,具體編程時要注意.
2.記錄型信號量典型應用:注意,這里block,wake在linux中都沒有,這里只是理論上的一個函數名字,下面我會講linux下的p,v操作函數
3.Linux下的pv操作
補充:
sem_t 是一種數據類型,用來定義記錄型信號量的,內部含有阻塞隊列
sem_init函數是用來初始化記錄型信號量的
eg:
sem_t a;
sem_init(&a,0,1); //初始化一個只有一個資源的記錄型信號量
wait相當於linux下的sem_wait,signal相當於linux下的sem_post
信號量使用完,必須銷毀,在主線程中使用sem_destroy函數銷毀
eg:
seg_destroy(&a);
4.實例:
這個策略,什么信號量都適合使用,包括后面的AND信號量和信號量集,都是基於這個原理
題目說明:盤子中一直只能有一個水果,實現同步
題目分析:我們采用記錄型信號量解決這個問題
1.在問題域中找臨界資源,
發現爸爸,媽媽關注盤子資源是不是(為空)能放水果,所以盤子(plate)是臨界資源
女兒只關心能不能吃到蘋果(apple),所以對女兒來說蘋果是一個臨界資源
兒子只關心能不能吃到橘子(orange),所以對於兒子來說橘子就是一個臨界資源
所以臨界資源有:plate,apple,orange
2.為每一個臨界資源定義一個信號量
plate:盤子,初始時,只有1個
apple:蘋果,初始時,有0個
orange:橘子,初始時,有0個
3,4步在具體編程中使用,切記切記,一定遵循這個原則
分析,父親母親(wait盤子,signal水果),占用盤子資源放水果,
當兒子或女兒吃完水果時(wait水果,signal盤子),應該由兒子或女兒釋放盤子資源
綜上,可是現題目中的同步
源代碼:
//m4.cpp 創建方法:vim m4.cpp 編譯方法:g++ m4.cpp -lpthread -o m4 執行方法: ./m4
#include <stdio.h> #include <pthread.h> #include <semaphore.h> sem_t plate,orange,apple;//定義三個信號量
//void* 表示返回值可以是任意類型的指針, 形參中的void *p表示傳入的形參可以是任意類型的指針,可用於傳遞多個參數
void *father(void *p) //父親函數,用於創建線程 { while(1) { sem_wait(&plate); //3,4原則,父親線程先判斷是不是有盤子資源 printf("the father put a apple\n"); sem_post(&apple); //父親,放完蘋果,盤子中蘋果數目+1=1,如果你這里有疑問請看最上面signal函數的偽代碼邏輯 } return NULL; } void *mother(void *p) //同理 { while(1) { sem_wait(&plate); printf("the mother put a orange\n"); sem_post(&orange); } return NULL; } void *daughter(void *p) { while(1) { sem_wait(&apple); //女兒判斷盤子中是不是有蘋果 printf("the daughter eats a apple\n"); sem_post(&plate); //女兒,吃完蘋果后,應該釋放盤子資源,為了父親,母親能在放水果 } return NULL; } void *son(void *p) { while(1) { sem_wait(&orange); printf("the son eats a orange\n"); sem_post(&plate); } return NULL; } int main() { sem_init(&plate,0,1);//初始化盤子臨界資源,初始1個
sem_init(&orange,0,0);//初始化橘子臨界資源,初始0個
sem_init(&apple,0,0);//初始化蘋果臨界資源,初始0個
pthread_t tid[4]; //創建四個進程 pthread_create(&tid[0],NULL,&father,NULL); pthread_create(&tid[1],NULL,&mother,NULL); pthread_create(&tid[2],NULL,&daughter,NULL); pthread_create(&tid[3],NULL,&son,NULL); pthread_join(tid[0],NULL); //主線程阻塞,等待子進程(線程執行),主線程不阻塞的話,會直接執行完
sem_destroy(&plate); //銷毀信號量 sem_destroy(&apple); sem_destroy(&orange); return 0; }
執行結果:死循環
5.AND型信號量和信號量集
進程/線程同步准則:這里不區分進程/線程,因為,引入了線程后線程就是資源調度的基本單位了,所以同步也可以指進程的線程同步
1.空閑讓進:
2.忙則等待:
3.有限等待
4.讓權等待:優先權低的進程讓優先權高的的進程先執行
AND型信號量:可以避免死鎖
為了解決一次分配多中資源,每種資源每次分配一個,一次獲得進程所需要的所有資源(每種個1個),否則進程阻塞,是記錄型信號量上的進一步延伸
AND型信號量的阻塞隊列機制,為每種資源設置一個阻塞隊列,當最先出現資源不足的資源種類為Ri時,那么進程就被阻塞在Ri資源對應的阻塞隊列中
信號量集:申請n類資源,每類資源最低ti個,每類申請di個資源
AND的進一步延伸,設置一個最低資源數目>=1,和進程需要的資源數目>=0
6.記錄型信號量和信號量集比較