日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

采用信号量机制实现消费者与生产者的线程同步_你还能聊聊常用的进程同步算法? 上篇[五]...

發(fā)布時間:2024/9/18 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 采用信号量机制实现消费者与生产者的线程同步_你还能聊聊常用的进程同步算法? 上篇[五]... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

點擊上方?“?布衣碼農(nóng)?”?,免費訂閱~選擇“?設(shè)為星標?”,第一時間免費獲得更新~

「布衣碼農(nóng)」進程同步的最佳實踐!

進程同步回顧

進程同步控制有多種方式:算法、硬件、信號量、管程;這些方式可以認為就是同步的工具(方法、函數(shù))。比如信號量機制中的wait(S) 和 signal(S) ,就相當于是兩個方法調(diào)用。調(diào)用wait(S)就會申請這個資源,否則就會等待(進入等待隊列);調(diào)用signal(S)就會釋放資源(或一并喚醒等待隊列中的某個);在梳理同步問題的解決思路時,只需要合理安排方法調(diào)用即可,底層的實現(xiàn)細節(jié)不需要關(guān)注。接下來以這種套路,看一下借助與不同的同步方式“算法、硬件、信號量、管程”這一“API”,如何解決經(jīng)典的進程同步問題

生產(chǎn)者消費者

生產(chǎn)者-消費者(producer-consumer)問題是一個著名的進程同步問題。它描述的是:有一群生產(chǎn)者進程在生產(chǎn)產(chǎn)品,并將這些產(chǎn)品提供給消費者進程去消費。為使生產(chǎn)者進程與消費者進程能并發(fā)執(zhí)行,在兩者之間設(shè)置了一個具有 n 個緩沖區(qū)的緩沖池,生產(chǎn)者進程將它所生產(chǎn)的產(chǎn)品放入一個緩沖區(qū)中;消費者進程可從一個緩沖區(qū)中取走產(chǎn)品去消費。盡管所有的生產(chǎn)者進程和消費者進程都是以異步方式運行的,但它們之間必須保持同步,也就是即不允許消費者進程到一個空緩沖區(qū)去取產(chǎn)品,也不允許生產(chǎn)者進程向一個已裝滿產(chǎn)品且尚未被取走的緩沖區(qū)中投放產(chǎn)品。

記錄型信號量

對于緩沖池本身,可以借助一個互斥信號量mutex實現(xiàn)各個進程對緩沖池的互斥使用;生產(chǎn)者關(guān)注于緩沖池空位子的個數(shù),消費者關(guān)注的是緩沖池中被放置好產(chǎn)品的滿的個數(shù)所以,我們總共設(shè)置三個信號量semaphore:mutex值為1,用于進程間互斥訪問緩沖池;full表示緩沖區(qū)這一排坑中被放置產(chǎn)品的個數(shù),初始時為0;empty表示緩沖區(qū)中空位子的個數(shù),初始時為n;對于緩沖池以一個數(shù)組的形式進行描述:buffer[n]另外還需要定義兩個用于對數(shù)組進行訪問的下標?in?和?out?,初始時都是0,也就是生產(chǎn)者會往0號位置放置元素,消費者會從0號開始取每次的操作之后,下標后移,in和out采用自增的方式,所以應該是循環(huán)設(shè)置,比如in為10時,應該從頭再來,所以求余(簡言之in out序號一直自增,通過求余循環(huán)) //變量定義int in=0, out=0;item buffer[n];semaphore mutex=l,empty=n, full=0;//生產(chǎn)者void proceducer(){do{producer an item nextp;......wait(empty);//等待空位子wait(mutex);//等待緩沖池可用buffer[in] =nextp;//設(shè)置元素in =(in+1)%n;//下標后移signal(mutex);//釋放緩沖池signal(full);//“滿”也就是已生產(chǎn)產(chǎn)品個數(shù)釋放1個(+1)}while(TRUE); //消費者void consumer() {do{wait(full);//等待已生產(chǎn)資源個數(shù)wait(mutex);//等待緩沖池可用nextc= buffer[out];//獲得一個元素out =(out+1) % n;//下標后移signal(mutex);//釋放緩沖池signal(empty);//空位子多出來一個consumer the item in nextc;//消費掉獲得的產(chǎn)品} while(TRUE);} //主程序void main() {proceducer();consumer();} 以上就是一個記錄型信號量解決生產(chǎn)者消費者的問題的思路對于信號量中用于實現(xiàn)互斥的wait和signal必須是成對出現(xiàn)的,盡管他們可能位于不同的程序中,這都無所謂,他們使用信號量作為紐帶進行聯(lián)系。

AND型信號量

對于生產(chǎn)者和消費者,都涉及兩種資源,一個是緩沖池,一個是緩沖池空或滿所以可以將上面兩種資源申請的步驟轉(zhuǎn)換為AND型,比如wait(empty);//等待空位子wait(mutex);//等待緩沖池可用轉(zhuǎn)換為AND的形式的Swait(empty,mutex)int in=0, out=0;item buffer[n];semaphore mutex=l, empty=n, full=O;void proceducer() {do{producer an item nextp;......Swait(empty, mutex);buffer[in] = nextp;in =(in+1) % n;Ssignal(mutex, full)} while(TRUE);}void consumer() {do{Swait(full, mutex);nextc= buffer[out];out =(out+1) % n;Ssignal(mutex, empty);consumer the item in nextc;......} while(TRUE);}這個示例中,AND型信號量方案只是記錄型信號量機制的一個簡單升級

管程方案

管程由一組共享數(shù)據(jù)結(jié)構(gòu)以及過程,還有條件變量組成。共享的數(shù)據(jù)結(jié)構(gòu)就是緩沖池,大小為n生產(chǎn)者向緩沖池中放入產(chǎn)品,定義過程put(item)消費者從緩沖池中取出產(chǎn)品,定義過程get(item)對于生產(chǎn)者,非滿 not full 就可以繼續(xù)生產(chǎn)數(shù)據(jù);對于消費者,非空 not empty 就可以繼續(xù)消費數(shù)據(jù);所以設(shè)置兩個條件:notfull,notempty如果數(shù)據(jù)個數(shù) count>=N,那么 notfull 非滿條件不成立如果數(shù)據(jù)個數(shù) count<=0,那么notempty 非空條件不成立也就是說:count>=N,notfull 不滿足,生產(chǎn)者就會在 notfull 條件上等待count<=0N,notempty 不滿足,消費者就會在 notempty 條件上等待//定義一個管程Monitor procducerconsumer {item buffer[N];//緩沖區(qū)大小int in, out;//訪問下標condition notfull, notempty;//條件變量int count;//已生產(chǎn)產(chǎn)品的個數(shù)//生產(chǎn)方法void put(item x) {if(count>=N){notfull.wait; //如果生產(chǎn)個數(shù)已經(jīng)大于緩沖區(qū)大小,將生產(chǎn)進程添加到notfull條件的等待隊列中}buffer[in] = x; //設(shè)置元素in = (in+1) % N; //下標移動count++;//已生產(chǎn)產(chǎn)品個數(shù)+1notempty.signal //釋放等待notempty條件的進程}//獲取方法void get(item x) {if(count<=0){notempty.wait; // 如果已生產(chǎn)產(chǎn)品數(shù)量為0(以下),消費者進程添加到notempty的等待隊列中}x = buffer[out];// 讀取元素out = (out+1) % N; // 下標移動count--; //已生產(chǎn)產(chǎn)品個數(shù)-1notfull.signal; // 釋放等待notfull條件的進程}//初始化數(shù)據(jù)方法void init(){in=0;out=0;count=0;}} PC;生產(chǎn)者和消費者邏輯void producer(){item x;while(TRUE){produce an item in nextp;PC.put(x);}}void consumer( {item x;while(TRUE) {PC.get(x);consume the item in nextc;......}}void main(){proceducer();consumer();}管程的解決思路就是將同步的問題封裝在管程內(nèi)部,管程會幫你解決所有的問題

哲學家進餐

由Dijkstra提出并解決的哲學家進餐問題(The Dinning Philosophers Problem)是典型的同步問題。該問題是描述有五個哲學家共用一張圓桌,分別坐在周圍的五張椅子上,在圓桌上有五個碗和五只筷子,他們的生活方式是交替地進行思考和進餐。平時,一個哲學家進行思考,饑餓時便試圖取用其左右最靠近他的筷子,只有在他拿到兩只筷子時才能進餐。進餐完畢,放下筷子繼續(xù)思考。?灰色大圓桌,黃色凳子,每個人左右各有一根筷子,小圓點表示碗。(盡管畫的像烏龜,但這真的是桌子  ̄□ ̄||)

記錄型信號量機制

放在桌子上的筷子是臨界資源,同一根筷子不可能被兩個人同時使用,所以每一根筷子都是一個共享資源需要使用五個信號量表示,五個信號量每個表示一根筷子當哲學家饑餓時,總是先去拿他左邊的筷子,即執(zhí)行wait(chopstick[i]);?成功后,再去拿他右邊的筷子,即執(zhí)行wait(chopstick[(i+1)mod 5]);又成功后便可進餐。(i+1)mod 5 是為了處理第五個人右邊的是第一個的問題 )進餐完畢,又先放下他左邊的筷子,然后再放右邊的筷子。//定義五個信號量//為簡單起見,假定數(shù)組起始下標為1//信號量全部初始化為1semaphore chopstick[5]={1,1,1,1,1};do{//按照我們上面圖中所示,第 i號哲學家,左手邊為i號筷子,右手邊是 (i+1)%5wait(chopstick[i]);//等待左手邊的,wait(chopstick[(i+1)%5]);]);//等待右手邊的// 進餐......signal(chopstick[i]);//釋放左手邊的signal(chopstick[(i+1)%5])//釋放右手邊的// 思考......} while(TRUE);通過這種算法可以保證相鄰的兩個哲學家之間不會出現(xiàn)問題,但是一旦五個人同時拿起左邊的筷子,都等待右邊的筷子,將會出現(xiàn)死鎖有幾種解決思路:(1)至多只允許有四位哲學家同時去拿左邊的筷子可以保證肯定會空余一根筷子,并且沒拿起筷子的這個人的左手邊的這一根,肯定是已經(jīng)拿起左手邊筷子的某一個人的右手邊,所以肯定不會死鎖(2)? 僅當哲學家的左、右兩只筷子均可用時,才允許他拿起筷子進餐。也就是AND機制,將左右操作轉(zhuǎn)化為“原子”(3)? 規(guī)定奇數(shù)號哲學家先拿他左邊的筷子,然后再去拿右邊的筷子,而偶數(shù)號哲學家則相反。如上圖所示,1搶1號筷子,2號和3號哲學家競爭3號筷子,4號和5號哲學家競爭5號筷子,所有人都是先競爭奇數(shù),然后再去競爭偶數(shù)這一條是為了所有的人都會先競爭奇數(shù)號筷子,那么也就是最多三個人搶到了奇數(shù)號筷子,有兩個人第一步奇數(shù)號筷子都沒搶到的這一輪就相當于出局了三個人,還有兩個偶數(shù)號筷子,必然會有一個人搶得到

AND型信號量

哲學家進餐需要左手和右手的筷子,所以可以將左右手筷子的獲取操作原子化,借助于AND型信號量//定義五個信號量//為簡單起見,假定數(shù)組起始下標為1//信號量全部初始化為1semaphore chopstick[5]={1,1,1,1,1};do{//按照我們上面圖中所示,第 i號哲學家,左手邊為i號筷子,右手邊是 (i+1)%5Swait(chopstick[i],chopstick[(i+1)%5]))// 進餐......Ssignal(chopstick[i],chopstick[(i+1)%5]);// 思考......} while(TRUE);

讀者寫者問題

一個數(shù)據(jù)文件或記錄,可被多個進程共享,我們把只要求讀該文件的進程稱為“Reader進程” ,其他進程則稱為“Writer 進程” 。允許多個進程同時讀一個共享對象,因為讀操作不會使數(shù)據(jù)文件混亂。但不允許一個Writer 進程和其他Reader 進程或 Writer 進程同時訪問共享對象,因為這種訪問將會引起混亂。所謂“讀者—寫者問題(Reader-Writer ?Problem)”是指保證一個 Writer 進程必須與其他進程互斥地訪問共享對象的同步問題。讀者—寫者問題常被用來測試新同步原語。?很顯然,只有多個讀者時不沖突

記錄型信號量機制

讀和寫之間是互斥的,所以需要一個信號量用于讀寫互斥Wmutex另外如果有讀的進程存在,另外的進程如果想要讀的話,不需要同步也就是Wait(Wmutex)操作;如果當前沒有進程在讀,那么需要Wait(Wmutex)操作,所以設(shè)置一個變量記錄寫者個數(shù)Readcount,可以用來判斷是否需要同步另外Readcount 會被多個讀者進程訪問,所以也是臨界資源,所以設(shè)置一個rmutex 用于互斥訪問Readcount//兩個信號量,一個用于讀者互斥 readcount ,一個用于讀寫互斥semaphore rmutex=l,wmutex=1;int readcount=0;//初始時讀者個數(shù)為0//讀者void reader() {do{wait(rmutex);//讀者先獲取 readcountif(readcount==0){//如果一個讀者沒有,第一個讀者需要與寫者互斥訪問wait(wmutex);}readcount++;//讀者個數(shù)+1signal(rmutex);//讀者個數(shù)+1后,可以釋放readcount的鎖,其他讀者可以進來//開始慢慢讀書......wait(rmutex);//讀者結(jié)束時,需要獲取readcount的鎖readcount--;//退出一個讀者if (readcount==0) {//如果此時一個讀者都沒有了,還需要釋放與讀寫互斥的鎖signal(wmutex);}signal(rmutex);//釋放readcount的鎖}while(TRUE);}void writer(){do{wait(wmutex);//寫者必須獲得wmutex//執(zhí)行寫任務(wù)....signal(wmutex);//寫任務(wù)結(jié)束后就可以釋放鎖}while(TRUE);}//主程序void main() {reader();writer();}寫者相對比較簡單,獲得鎖wmutex之后,進行寫操作,否則等待wmutex.讀者也是需要先獲得鎖,讀操作后釋放鎖,但是因為多個讀者之間互不影響,所以使用readcount記錄讀者個數(shù),只有第一個讀者才需要競爭wmutex,只有最后一個讀者才需要釋放wmutex;readcount作為讀者之間的競爭資源,所以對readcount進行操作的時候也需要進行加鎖;

信號量集機制

將讀者寫者的問題復雜化一點,它增加了一個限制,即最多只允許 N個讀者同時讀。在上面的解決方法中,可以不使用rmutex控制對readcount的互斥,可以構(gòu)造一個讀者個數(shù)的信號量readcountmutex,初始值設(shè)置為N每次新增一個讀者時,wait(readcountmutex);一個讀者離開時signal(readcountmutex);也可以使用信號量集機制int?N;//最大的讀者個數(shù),也就是相當于圖書館的空位子,初始時空位子為Nsemaphore L=N, mx=1;//定義兩個信號量資源L和mx,分別用于控制讀者個數(shù)限制和讀寫(寫寫)void reader() {do{Swait(L, 1, 1);//獲取空位子L,每次獲取1個,>=1時可分配Swait(mx, 1, 0);//獲取與寫的互斥量mx,每次獲取0個,>=1時可分配,如果mx為1,也就是沒有寫者,讀者都可以進來,否則一個都進不來//進行一些讀操作Ssignal(L, 1);//釋放一個單位的資源L}while(TRUE);}void?writer()?{do{?? Swait(mx,1,1;?L,N,0);//獲得資源mx,每次獲取1個,>=1時分配,獲得資源L,每次獲得0個,>=N時即可分配? ?//進行一些寫操作?? Ssignal(mx,?1);//釋放資源mx????}while(TRUE);}void?main(){reader();writer();}Swait(L, 1, 1);用于獲取讀者空位子沒什么好說的;Swait(mx, 1, 0);作為開關(guān),只要mx滿足條件>=1,那么就可以無限制的進入(此例中有L的限制),一旦條件不滿足,則全都不能進入,滿足多讀者,有寫不能讀的情況對于寫者中的Swait(mx,1,1; L,N,0);他會獲取mx,>=1時,獲取一個資源,并且當L>=N時,分配0個L資源,也就是說一個讀者都沒有的時候才行Swait(mx, 1, 0); 與Swait( L,N,0);都是需求0個,相當于開關(guān)判斷

總結(jié)

以上為借助“進程同步的API”,信號量,管程等方式完成進程同步的經(jīng)典示例,例子來源于《計算機操作系統(tǒng)》說白了,就是用 wait(S)?? Swait(S)?? signal(S)?? Ssignal(S)等這些“方法”描述進程同步算法。可能會覺得這些內(nèi)容亂七八糟的,根本沒辦法使用,的確這些內(nèi)容全都沒辦法直接轉(zhuǎn)變?yōu)榇a寫到你的項目中,但是,這些都是解決問題的思路。不管是信號量還是管程還是什么,不會需要你從頭開始實現(xiàn)一個信號量,然后.......也不需要你從頭開始實現(xiàn)一個管程,然后......不管是操作系統(tǒng)層面,還是編程語言層面,還是具體的API,萬變不離其宗盡管這些wait和signal的確不存在,但是,但是,但是編程語言中很可能已經(jīng)提供了語意相同的方法供你調(diào)用了也就是說,你只需要理解同步的思路即可,盡管沒有我們此處說的wait(S),但是肯定有對應物。

··················END··················

注:非技術(shù)講解配圖均來源于網(wǎng)絡(luò)

參考資料:《計算機操作系統(tǒng)》

期待分享

如果對你有用

可以點個?「在看」?或者分享到?「?朋友圈?」?哦

你「在看」嗎??↓↓

總結(jié)

以上是生活随笔為你收集整理的采用信号量机制实现消费者与生产者的线程同步_你还能聊聊常用的进程同步算法? 上篇[五]...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。