詳解進(jìn)程同步與互斥機(jī)制
一、什么是進(jìn)程同步
在多道批處理系統(tǒng)中,多個(gè)進(jìn)程是可以并發(fā)執(zhí)行的,但由于系統(tǒng)的資源有限,進(jìn)程的執(zhí)行不是一貫到底的, 而是走走停停,以不可預(yù)知的速度向前推進(jìn),這就是進(jìn)程的異步性。
那么,進(jìn)程的異步性會(huì)帶來什么問題呢?舉個(gè)例子,如果有 A、B 兩個(gè)進(jìn)程分別負(fù)責(zé)讀和寫數(shù)據(jù)的操作,這兩個(gè)線程是相互合作、相互依賴的。那么寫數(shù)據(jù)應(yīng)該發(fā)生在讀數(shù)據(jù)之前。而實(shí)際上,由于異步性的存在,可能會(huì)發(fā)生先讀后寫的情況,而此時(shí)由于緩沖區(qū)還沒有被寫入數(shù)據(jù),讀進(jìn)程 A 沒有數(shù)據(jù)可讀,因此讀進(jìn)程 A 被阻塞。

進(jìn)程同步(synchronization)就是用來解決這個(gè)問題的。從上面的例子我們能看出,一個(gè)進(jìn)程的執(zhí)行可能影響到另一個(gè)進(jìn)程的執(zhí)行,所謂進(jìn)程同步就是指協(xié)調(diào)這些完成某個(gè)共同任務(wù)的并發(fā)線程,在某些位置上指定線程的先后執(zhí)行次序、傳遞信號(hào)或消息。
再舉個(gè)生活中的進(jìn)程同步的例子,你想要喝熱水,于是你打了一壺水開始燒,在這壺水燒開之前,你只能一直等著,水燒開之后水壺自然會(huì)發(fā)生響聲提醒你來喝水,于是你就可以喝水了。就是說水燒開這個(gè)事情必須發(fā)生在你喝水之前。
注意不要把進(jìn)程同步和進(jìn)程調(diào)度搞混了:
- 進(jìn)程調(diào)度是為了最大程度的利用 CPU 資源,選用合適的算法調(diào)度就緒隊(duì)列中的進(jìn)程。
- 進(jìn)程同步是為了協(xié)調(diào)一些進(jìn)程以完成某個(gè)任務(wù),比如讀和寫,你肯定先寫后讀,不能先讀后寫吧,這就是進(jìn)程同步做的事情了,指定這些進(jìn)程的先后執(zhí)行次序使得某個(gè)任務(wù)能夠順利完成。
二、什么是進(jìn)程互斥
同樣的,也是因?yàn)檫M(jìn)程的并發(fā)性,并發(fā)執(zhí)行的線程不可避免地需要共享一些系統(tǒng)資源,比如內(nèi)存、打印機(jī)、攝像頭等。舉個(gè)例子:我們?nèi)W(xué)校打印店打印論文,你按下了 WPS 的 “打印” 選項(xiàng),于是打印機(jī)開始工作。 你的論文打印到一半時(shí),另一位同學(xué)按下了 Word 的 “打印” 按鈕,開始打印他自己的論文。想象一下如果兩個(gè)進(jìn)程可以隨意的、并發(fā)的共享打印機(jī)資源,會(huì)發(fā)生什么情況?
顯然,兩個(gè)進(jìn)程并發(fā)運(yùn)行,導(dǎo)致打印機(jī)設(shè)備交替的收到 WPS 和 Word 兩個(gè)進(jìn)程發(fā)來的打印請(qǐng)求,結(jié)果兩篇論文的內(nèi)容混雜在一起了。
進(jìn)程互斥(mutual exclusion)就是用來解決這個(gè)問題的。當(dāng)某個(gè)進(jìn)程 A 在訪問打印機(jī)時(shí),如果另一個(gè)進(jìn)程 B 也想要訪問打印機(jī),它就必須等待,直到 A 進(jìn)程訪問結(jié)束并釋放打印機(jī)資源后,B 進(jìn)程才能去訪問。
實(shí)際上,像上述的打印機(jī)這種在一個(gè)時(shí)間段內(nèi)只允許一個(gè)進(jìn)程使用的資源(這也就是互斥的意思),我們將其稱為臨界資源,對(duì)臨界資源進(jìn)行訪問的那段代碼稱為臨界區(qū)。

通俗的對(duì)比一下進(jìn)程互斥和進(jìn)程同步:
- 進(jìn)程同步:進(jìn)程 A 應(yīng)在進(jìn)程 B 之前執(zhí)行
- 進(jìn)程互斥:進(jìn)程 A 和進(jìn)程 B 不能在同一時(shí)刻執(zhí)行
從上不難看出,進(jìn)程互斥是一種特殊的進(jìn)程同步,即逐次使用臨界資源,也是對(duì)進(jìn)程使用資源的先后執(zhí)行次序的一種協(xié)調(diào)。
三、常見的進(jìn)程同步與互斥機(jī)制
常見的進(jìn)程同步與互斥機(jī)制有兩種:
- 信號(hào)量與 PV 操作
- 管程
① 信號(hào)量與 PV 操作
包交包會(huì)!看完下面這段解釋你絕對(duì)能夠明白 PV 操作是啥。
1965年,荷蘭學(xué)者 Dijkstra 提出了一種卓有成效的實(shí)現(xiàn)進(jìn)程同步和互斥的方法 — 信號(hào)量機(jī)制(Semaphore)。信號(hào)量其實(shí)就是一個(gè)變量 ,我們可以用一個(gè)信號(hào)量來表示系統(tǒng)中某種資源的數(shù)量,比如:系統(tǒng)中只有一臺(tái)打印機(jī),就可以設(shè)置一個(gè)初值為 1 的信號(hào)量。
用戶進(jìn)程可以通過使用操作系統(tǒng)提供的一對(duì)原語來對(duì)信號(hào)量進(jìn)行操作,從而很方便的實(shí)現(xiàn)進(jìn)程互斥或同步。這一對(duì)原語就是 PV 操作:
1)P 操作:將信號(hào)量值減 1,表示申請(qǐng)占用一個(gè)資源。如果結(jié)果小于 0,表示已經(jīng)沒有可用資源,則執(zhí)行 P 操作的進(jìn)程被阻塞。如果結(jié)果大于等于 0,表示現(xiàn)有的資源足夠你使用,則執(zhí)行 P 操作的進(jìn)程繼續(xù)執(zhí)行。
可以這么理解,當(dāng)信號(hào)量的值為 2 的時(shí)候,表示有 2 個(gè)資源可以使用,當(dāng)信號(hào)量的值為 -2 的時(shí)候,表示有兩個(gè)進(jìn)程正在等待使用這個(gè)資源。不看這句話真的無法理解 V 操作,看完頓時(shí)如夢(mèng)初醒。
2)V 操作:將信號(hào)量值加 1,表示釋放一個(gè)資源,即使用完資源后歸還資源。若加完后信號(hào)量的值小于等于 0,表示有某些進(jìn)程正在等待該資源,由于我們已經(jīng)釋放出一個(gè)資源了,因此需要喚醒一個(gè)等待使用該資源(就緒態(tài))的進(jìn)程,使之運(yùn)行下去。
我覺得已經(jīng)講的足夠通俗了,不過對(duì)于 V 操作大家可能仍然有困惑,下面再來看兩個(gè)關(guān)于 V 操作的問答:
問:信號(hào)量的值 大于 0 表示有臨界資源可供使用,這個(gè)時(shí)候?yàn)槭裁床恍枰獑拘堰M(jìn)程?
答:所謂喚醒進(jìn)程是從就緒隊(duì)列(阻塞隊(duì)列)中喚醒進(jìn)程,而信號(hào)量的值大于 0 表示有臨界資源可供使用,也就是說這個(gè)時(shí)候沒有進(jìn)程被阻塞在這個(gè)資源上,所以不需要喚醒,正常運(yùn)行即可。
問:信號(hào)量的值 等于 0 的時(shí)候表示沒有臨界資源可供使用,為什么還要喚醒進(jìn)程?
答:V 操作是先執(zhí)行信號(hào)量值加 1 的,也就是說,把信號(hào)量的值加 1 后才變成了 0,在此之前,信號(hào)量的值是 -1,即有一個(gè)進(jìn)程正在等待這個(gè)臨界資源,我們需要喚醒它。
信號(hào)量和 PV 操作具體的定義如下:

實(shí)現(xiàn)進(jìn)程互斥
兩步走即可實(shí)現(xiàn)進(jìn)程的互斥:
- 定義一個(gè)互斥信號(hào)量,并初始化為 1
- 把對(duì)于臨界資源的訪問置于 P 操作和 V 操作之間

P 操作和 V 操作必須成對(duì)出現(xiàn)。缺少 P 操作就不能保證對(duì)臨界資源的互斥訪問,缺少 V 操作就會(huì)導(dǎo)致臨界資源永遠(yuǎn)得不到釋放、處于等待態(tài)的進(jìn)程永遠(yuǎn)得不到喚醒。
實(shí)現(xiàn)進(jìn)程同步
回顧一下進(jìn)程同步,就是要各并發(fā)進(jìn)程按要求有序地運(yùn)行。
舉個(gè)例子,以下兩個(gè)進(jìn)程 P1、P2 并發(fā)執(zhí)行,由于存在異步性,因此二者交替推進(jìn)的次序是不確定的。假設(shè) P2 的 “代碼4” 要基于 P1 的 “代碼1” 和 “代碼2” 的運(yùn)行結(jié)果才能執(zhí)行,那么我們就必須保證 “代碼4” 一定是在 “代碼2” 之后才會(huì)執(zhí)行。

如果 P2 的 “代碼4” 要基于 P1 的 “代碼1” 和 “代碼2” 的運(yùn)行結(jié)果才能執(zhí)行,那么我們就必須保證 “代碼4” 一定是在 “代碼2” 之后才會(huì)執(zhí)行。
使用信號(hào)量和 PV 操作實(shí)現(xiàn)進(jìn)程的同步也非常方便,三步走:
- 定義一個(gè)同步信號(hào)量,并初始化為當(dāng)前可用資源的數(shù)量
- 在優(yōu)先級(jí)較高的操作的后面執(zhí)行 V 操作,釋放資源
- 在優(yōu)先級(jí)較低的操作的前面執(zhí)行 P 操作,申請(qǐng)占用資源

配合下面這張圖直觀理解下:

生產(chǎn)者和消費(fèi)者問題
下面我們利用信號(hào)量和 PV 操作來解決經(jīng)典的進(jìn)程同步和互斥問題:生產(chǎn)者和消費(fèi)者問題。
【問題描述】:系統(tǒng)中有一組生產(chǎn)者進(jìn)程和一組消費(fèi)者進(jìn)程,生產(chǎn)者進(jìn)程每次生產(chǎn)一個(gè)產(chǎn)品放入緩沖區(qū),消費(fèi)者進(jìn)程每次從緩沖區(qū)中取出一個(gè)產(chǎn)品并使用。任何時(shí)刻,只能有一個(gè)生產(chǎn)者或消費(fèi)者可以訪問緩沖區(qū)。

由題可知,生產(chǎn)者、消費(fèi)者共享一個(gè)初始為空、大小為 n 的緩沖區(qū),我們從題目中提煉出同步與互斥關(guān)系:
- 同步關(guān)系 1:只有緩沖區(qū)沒滿時(shí)(優(yōu)先級(jí)高),生產(chǎn)者才能把產(chǎn)品放入緩沖區(qū)(優(yōu)先級(jí)低),否則必須等待
- 同步關(guān)系 2:只有緩沖區(qū)不空時(shí)(優(yōu)先級(jí)高),消費(fèi)者才能從中取出產(chǎn)品(優(yōu)先級(jí)低),否則必須等待
- 互斥關(guān)系:緩沖區(qū)是臨界資源,各進(jìn)程必須互斥地訪問。
既然這個(gè)題目有兩個(gè)同步關(guān)系和一個(gè)互斥關(guān)系,那么我們就需要兩個(gè)同步信號(hào)量和一個(gè)互斥信號(hào)量:
- empty:同步信號(hào)量(對(duì)應(yīng)同步關(guān)系 1),表示生產(chǎn)者還能生產(chǎn)多少,即還能放入緩沖區(qū)多少產(chǎn)品,該數(shù)量小于等于 0,則生產(chǎn)者不能進(jìn)行生產(chǎn)。 初始化為 n。
- full:同步信號(hào)量(對(duì)應(yīng)同步關(guān)系 2),表示消費(fèi)者還能從緩沖區(qū)取出多少,即當(dāng)前緩沖區(qū)已有產(chǎn)品的數(shù)量,該數(shù)量小于等于 0,則消費(fèi)者不能進(jìn)行讀取。初始化為 0。
- mutex:互斥信號(hào)量,實(shí)現(xiàn)對(duì)緩沖區(qū)的互斥訪問。初始化為 1。
代碼如下,注意各個(gè) PV 操作的配對(duì):

② 管程
管程有一個(gè)重要特性:在一個(gè)時(shí)刻只能有一個(gè)進(jìn)程使用管程。進(jìn)程在無法繼續(xù)執(zhí)行的時(shí)候不能一直占用管程,否則其它進(jìn)程將永遠(yuǎn)不能使用管程。也就是說管程天生支持進(jìn)程互斥。
其實(shí)使用管程是能夠?qū)崿F(xiàn)信號(hào)量的,并且也能用信號(hào)量實(shí)現(xiàn)管程。但是管程封裝的比較好,相比起信號(hào)量來需要我們編寫的代碼更少,更加易用,這也就是 Java 采用管程機(jī)制的原因,synchronized 關(guān)鍵字及 wait()、notify()、notifyAll() 這三個(gè)方法都是管程的組成部分。把管程翻譯為 Java 領(lǐng)域的語言,就是管理類的成員變量和成員方法,讓這個(gè)類是線程安全的。
以上就是詳解進(jìn)程同步與互斥機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于進(jìn)程同步與互斥機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
DSP中浮點(diǎn)轉(zhuǎn)定點(diǎn)運(yùn)算--舉例及編程中的心得
本文主要講解DSP浮點(diǎn)轉(zhuǎn)定點(diǎn)運(yùn)算舉例及編程中的心得 ,具有參考價(jià)值,需要的朋友可以參考一下。2016-06-06
C++實(shí)現(xiàn)字符串和整數(shù)的相互轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)字符串和整數(shù)的相互轉(zhuǎn)換的方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C++有一定的幫助,需要的可以參考一下2023-01-01

