詳解進程同步與互斥機制

一、什麼是進程同步

在多道批處理系統中,多個進程是可以並發執行的,但由於系統的資源有限,進程的執行不是一貫到底的, 而是走走停停,以不可預知的速度向前推進,這就是進程的異步性。

那麼,進程的異步性會帶來什麼問題呢?舉個例子,如果有 A、B 兩個進程分別負責讀和寫數據的操作,這兩個線程是相互合作、相互依賴的。那麼寫數據應該發生在讀數據之前。而實際上,由於異步性的存在,可能會發生先讀後寫的情況,而此時由於緩沖區還沒有被寫入數據,讀進程 A 沒有數據可讀,因此讀進程 A 被阻塞。

進程同步(synchronization)就是用來解決這個問題的。從上面的例子我們能看出,一個進程的執行可能影響到另一個進程的執行,所謂進程同步就是指協調這些完成某個共同任務的並發線程,在某些位置上指定線程的先後執行次序、傳遞信號或消息。

再舉個生活中的進程同步的例子,你想要喝熱水,於是你打瞭一壺水開始燒,在這壺水燒開之前,你隻能一直等著,水燒開之後水壺自然會發生響聲提醒你來喝水,於是你就可以喝水瞭。就是說水燒開這個事情必須發生在你喝水之前。

註意不要把進程同步和進程調度搞混瞭:

  • 進程調度是為瞭最大程度的利用 CPU 資源,選用合適的算法調度就緒隊列中的進程。
  • 進程同步是為瞭協調一些進程以完成某個任務,比如讀和寫,你肯定先寫後讀,不能先讀後寫吧,這就是進程同步做的事情瞭,指定這些進程的先後執行次序使得某個任務能夠順利完成。

二、什麼是進程互斥

同樣的,也是因為進程的並發性,並發執行的線程不可避免地需要共享一些系統資源,比如內存、打印機、攝像頭等。舉個例子:我們去學校打印店打印論文,你按下瞭 WPS 的 “打印” 選項,於是打印機開始工作。 你的論文打印到一半時,另一位同學按下瞭 Word 的 “打印” 按鈕,開始打印他自己的論文。想象一下如果兩個進程可以隨意的、並發的共享打印機資源,會發生什麼情況?

顯然,兩個進程並發運行,導致打印機設備交替的收到 WPS 和 Word 兩個進程發來的打印請求,結果兩篇論文的內容混雜在一起瞭。

進程互斥(mutual exclusion)就是用來解決這個問題的。當某個進程 A 在訪問打印機時,如果另一個進程 B 也想要訪問打印機,它就必須等待,直到 A 進程訪問結束並釋放打印機資源後,B 進程才能去訪問。

實際上,像上述的打印機這種在一個時間段內隻允許一個進程使用的資源(這也就是互斥的意思),我們將其稱為臨界資源,對臨界資源進行訪問的那段代碼稱為臨界區。

通俗的對比一下進程互斥和進程同步:

  • 進程同步:進程 A 應在進程 B 之前執行
  • 進程互斥:進程 A 和進程 B 不能在同一時刻執行

從上不難看出,進程互斥是一種特殊的進程同步,即逐次使用臨界資源,也是對進程使用資源的先後執行次序的一種協調。

三、常見的進程同步與互斥機制

常見的進程同步與互斥機制有兩種:

  • 信號量與 PV 操作
  • 管程

① 信號量與 PV 操作

包交包會!看完下面這段解釋你絕對能夠明白 PV 操作是啥。

1965年,荷蘭學者 Dijkstra 提出瞭一種卓有成效的實現進程同步和互斥的方法 — 信號量機制(Semaphore)。信號量其實就是一個變量 ,我們可以用一個信號量來表示系統中某種資源的數量,比如:系統中隻有一臺打印機,就可以設置一個初值為 1 的信號量。

用戶進程可以通過使用操作系統提供的一對原語來對信號量進行操作,從而很方便的實現進程互斥或同步。這一對原語就是 PV 操作:

1)P 操作:將信號量值減 1,表示申請占用一個資源。如果結果小於 0,表示已經沒有可用資源,則執行 P 操作的進程被阻塞。如果結果大於等於 0,表示現有的資源足夠你使用,則執行 P 操作的進程繼續執行。

可以這麼理解,當信號量的值為 2 的時候,表示有 2 個資源可以使用,當信號量的值為 -2 的時候,表示有兩個進程正在等待使用這個資源。不看這句話真的無法理解 V 操作,看完頓時如夢初醒。

2)V 操作:將信號量值加 1,表示釋放一個資源,即使用完資源後歸還資源。若加完後信號量的值小於等於 0,表示有某些進程正在等待該資源,由於我們已經釋放出一個資源瞭,因此需要喚醒一個等待使用該資源(就緒態)的進程,使之運行下去。

我覺得已經講的足夠通俗瞭,不過對於 V 操作大傢可能仍然有困惑,下面再來看兩個關於 V 操作的問答:

問:信號量的值 大於 0 表示有臨界資源可供使用,這個時候為什麼不需要喚醒進程?

答:所謂喚醒進程是從就緒隊列(阻塞隊列)中喚醒進程,而信號量的值大於 0 表示有臨界資源可供使用,也就是說這個時候沒有進程被阻塞在這個資源上,所以不需要喚醒,正常運行即可。

問:信號量的值 等於 0 的時候表示沒有臨界資源可供使用,為什麼還要喚醒進程?

答:V 操作是先執行信號量值加 1 的,也就是說,把信號量的值加 1 後才變成瞭 0,在此之前,信號量的值是 -1,即有一個進程正在等待這個臨界資源,我們需要喚醒它。

信號量和 PV 操作具體的定義如下:

實現進程互斥

兩步走即可實現進程的互斥:

  • 定義一個互斥信號量,並初始化為 1
  • 把對於臨界資源的訪問置於 P 操作和 V 操作之間

P 操作和 V 操作必須成對出現。缺少 P 操作就不能保證對臨界資源的互斥訪問,缺少 V 操作就會導致臨界資源永遠得不到釋放、處於等待態的進程永遠得不到喚醒。

實現進程同步

回顧一下進程同步,就是要各並發進程按要求有序地運行。

舉個例子,以下兩個進程 P1、P2 並發執行,由於存在異步性,因此二者交替推進的次序是不確定的。假設 P2 的 “代碼4” 要基於 P1 的 “代碼1” 和 “代碼2” 的運行結果才能執行,那麼我們就必須保證 “代碼4” 一定是在 “代碼2” 之後才會執行。

如果 P2 的 “代碼4” 要基於 P1 的 “代碼1” 和 “代碼2” 的運行結果才能執行,那麼我們就必須保證 “代碼4” 一定是在 “代碼2” 之後才會執行。

使用信號量和 PV 操作實現進程的同步也非常方便,三步走:

  • 定義一個同步信號量,並初始化為當前可用資源的數量
  • 在優先級較高的操作的後面執行 V 操作,釋放資源
  • 在優先級較低的操作的前面執行 P 操作,申請占用資源

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

生產者和消費者問題

下面我們利用信號量和 PV 操作來解決經典的進程同步和互斥問題:生產者和消費者問題。

【問題描述】:系統中有一組生產者進程和一組消費者進程,生產者進程每次生產一個產品放入緩沖區,消費者進程每次從緩沖區中取出一個產品並使用。任何時刻,隻能有一個生產者或消費者可以訪問緩沖區。

由題可知,生產者、消費者共享一個初始為空、大小為 n 的緩沖區,我們從題目中提煉出同步與互斥關系:

  • 同步關系 1:隻有緩沖區沒滿時(優先級高),生產者才能把產品放入緩沖區(優先級低),否則必須等待
  • 同步關系 2:隻有緩沖區不空時(優先級高),消費者才能從中取出產品(優先級低),否則必須等待
  • 互斥關系:緩沖區是臨界資源,各進程必須互斥地訪問。

既然這個題目有兩個同步關系和一個互斥關系,那麼我們就需要兩個同步信號量和一個互斥信號量:

  • empty:同步信號量(對應同步關系 1),表示生產者還能生產多少,即還能放入緩沖區多少產品,該數量小於等於 0,則生產者不能進行生產。 初始化為 n。
  • full:同步信號量(對應同步關系 2),表示消費者還能從緩沖區取出多少,即當前緩沖區已有產品的數量,該數量小於等於 0,則消費者不能進行讀取。初始化為 0。
  • mutex:互斥信號量,實現對緩沖區的互斥訪問。初始化為 1。

代碼如下,註意各個 PV 操作的配對:

② 管程

管程有一個重要特性:在一個時刻隻能有一個進程使用管程。進程在無法繼續執行的時候不能一直占用管程,否則其它進程將永遠不能使用管程。也就是說管程天生支持進程互斥。

其實使用管程是能夠實現信號量的,並且也能用信號量實現管程。但是管程封裝的比較好,相比起信號量來需要我們編寫的代碼更少,更加易用,這也就是 Java 采用管程機制的原因,synchronized 關鍵字及 wait()notify()notifyAll() 這三個方法都是管程的組成部分。把管程翻譯為 Java 領域的語言,就是管理類的成員變量和成員方法,讓這個類是線程安全的。

以上就是詳解進程同步與互斥機制的詳細內容,更多關於進程同步與互斥機制的資料請關註WalkonNet其它相關文章!

推薦閱讀: