程序通常分為就緒、執行和阻塞三個工作狀態。三種狀態在某些條件下可以轉換,三者之間的轉換關係如下:
程序三個狀態之間的轉換就是靠pv操作來控制的。pv操作主要就是p操作、v操作和訊號量。其中訊號量起到了至關重要的作用。
訊號量
訊號量是最早出現的用來解決程序同步與互斥問題的機制。
訊號量(saphore)由乙個值和乙個指標組成,指標指向等待該訊號量的程序。訊號量的值表示相應資源的使用情況。訊號量s>=0時,s表示可用資源的數量。執行一次p操作意味著請求分配乙個資源,因此s的值減1;當s<0時,表示已經沒有可用資源,s的絕對值表示當前等待該資源的程序數。請求者必須等待其他程序釋放該類資源,才能繼續執行。而執行乙個v操作意味著釋放乙個資源,因此s的值加1;若s<0,表示有某些程序正在等待該資源,因此要喚醒乙個等待狀態的程序,使之執行下去。
注意,訊號量的值只能由pv操作來改變。
關於pv操作容易產生的一些疑問:
1,s大於0那就表示有臨界資源可供使用,為什麼不喚醒程序?
s大於0的確表示有臨界資源可供使用,也就是說這個時候沒有程序被阻塞在這個資源上,所以不需要喚醒。
2,s小於0應該是說沒有臨界資源可供使用,為什麼還要喚醒程序?
v原語操作的本質在於:乙個程序使用完臨界資源後,釋放臨界資源,使s加1,以通知其它的程序,這個時候如果s<0,表明有程序阻塞在該類資源上,因此要從阻塞佇列裡喚醒乙個程序來「轉手」該類資源。比如,有兩個某類資源,四個程序a、b、c、d要用該類資源,最開始s=2,當a進入,s=1,當b進入s=0,表明該類資源剛好用完, 當c進入時s=-1,表明有乙個程序被阻塞了,d進入,s=-2。當a用完該類資源時,進行v操作,s=-1,釋放該類資源,因為s<0,表明有程序阻塞在該類資源上,於是喚醒乙個。
3,如果是互斥訊號量的話,應該設定訊號量s=1,但是當有5個程序都訪問的話,最後在該訊號量的煉表裡會有4個在等待,也是說s=-4,那麼第乙個程序執行了v操作使s加1,釋放了資源,下乙個應該能夠執行,但喚醒的這個程序在執行p操作時因s<0,也還是執行不了,這是怎麼回事呢?
當乙個程序阻塞了的時候,它已經執行過了p操作,並卡在臨界區那個地方。當喚醒它時就立即進入它自己的臨界區,並不需要執行p操作了,當執行完了臨界區的程式後,就執行v操作。
4,s的絕對值表示等待的程序數,同時又表示臨界資源,這到底是怎麼回事?
當訊號量s小於0時,其絕對值表示系統中因請求該類資源而被阻塞的程序數目.s大於0時表示可用的臨界資源數。注意在不同情況下所表達的含義不一樣。當等於0時,表示剛好用完。
關於pv操作部分的內容,其實算不上什麼新的東西。但是它對於我們理解訊號量、訊息處理部分的工作還是有很大幫助的。之前我們給出了乙個win32的處理方案,但是實現的比較草率。所以我們今天可以利用linux上的訊號量函式把這個功能重新實現一遍。
(1)linux下面訊號量的基本函式
a)建立訊號量 sem_init
b)等待訊號量 sem_wait
c)釋放訊號量 sem_pos
d)刪除訊號量 sem_destroy
(2)編寫pv操作函式
之前在編寫pv操作的時候,沒有考慮到訊息處理的時序問題,所以在某些極端的情況下可能會造成一些問題。所以本次pv操作採用了迴圈佇列的形式,保持了訊息的先後入隊順序。這樣對於執行緒收到的各種訊息就可以依次進行處理解決了。同樣,我們檔案編譯的方法非常簡單,shell下輸入gcc sem.c -g -o sem -lpthread即可。
可能有同學會問,單獨的迴圈佇列和pv操作處理上有什麼差別?其實差別很簡單,pv可以是不同執行緒向乙個執行緒傳送訊息,而迴圈佇列只能接受乙個執行緒傳送的訊息,否則處理上就麻煩了。
[cpp]view plain
copy
#include
#include
#include
#include
#include
struct
msg
; #define status int
#define true 1
#define false 0
static
struct
msg* p_msg = null;
struct
msg* alloc_msg(
intcount)
memset(p_msg, 0, sizeof
(struct
msg));
p_msg->count = count;
p_msg->p_buffer = (int
*)malloc(
sizeof
(int
)* count);
if(null == p_msg->p_buffer)
sem_init(&p_msg->s_empty, 0, count);
sem_init(&p_msg->s_full, 0, 0);
sem_init(&p_msg->s_msg, 0, 1);
return
p_msg;
error2:
free(p_msg);
error1:
return
; }
void
del_msg(
struct
msg* p_msg)
sem_destroy(&p_msg->s_msg);
sem_destroy(&p_msg->s_full);
sem_destroy(&p_msg->s_empty);
free(p_msg);
} }
status put_msg(struct
msg* p_msg,
intdata)
sem_wait(&p_msg->s_empty);
sem_wait(&p_msg->s_msg);
p_msg->p_buffer[p_msg->start] = data;
p_msg->start = (p_msg->start + 1) % p_msg->count;
sem_post(&p_msg->s_msg);
sem_post(&p_msg->s_full);
return
true;
} status get_msg(struct
msg* p_msg,
int* p_buf)
sem_wait(&p_msg->s_full);
sem_wait(&p_msg->s_msg);
p_buf[0] = p_msg->p_buffer[p_msg->end];
p_msg->end = (p_msg->end + 1)% p_msg->count;
sem_post(&p_msg->s_msg);
sem_post(&p_msg->s_empty);
return
true;
} void
* set_func(
void
* args)
return
null;
} void
* get_func(
void
* args)
return
null;
} int
main(
intargc,
char
* argv)
if(pthread_create(&pid1, null, set_func, null))
if(pthread_create(&pid2, null, get_func, null))
while
(1)
end:
return
1;
}
P,V操作理解
程序通常分為就緒 執行和阻塞三個工作狀態。三種狀態在某些條件下可以轉換,三者之間的轉換關係如下 程序三個狀態之間的轉換就是靠pv操作來控制的。pv操作主要就是p操作 v操作和訊號量。其中訊號量起到了至關重要的作用。訊號量 訊號量是最早出現的用來解決程序同步與互斥問題的機制。訊號量 saphore 由...
理解PV操作
程序通常分為就緒 執行和阻塞三個工作狀態。三種狀態在某些條件下可以轉換,三者之間的轉換關係如下 程序三個狀態之間的轉換就是靠pv操作來控制的。pv操作主要就是p操作 v操作和訊號量。其中訊號量起到了至關重要的作用。訊號量 訊號量是最早出現的用來解決程序同步與互斥問題的機制。訊號量 saphore 由...
理解MapReduce操作
1.用python編寫wordcount程式並提交任務 程式wordcount 輸入乙個包含大量單詞的文字檔案 輸出檔案中每個單詞及其出現次數 頻數 並按照單詞字母順序排序,每個單詞和其頻數佔一行,單詞和頻數之間有間隔 編寫map函式,reduce函式 usr bin env python impo...