C/C++中的程序同步
程序同步是一種克服對共享資料併發訪問問題的技術,併發訪問可能導致資料不一致。協作程序是可以影響或被其他程序影響的程序,這會導致程序資料不一致,因此需要程序同步來保證資料一致性。
臨界區問題
每個程序都有一個保留的程式碼段,稱為**臨界區**。在這個區域中,程序可以更改公共變數、更新表、寫入檔案等。關於臨界區的關鍵點是:當一個程序正在其臨界區執行時,任何其他程序都不能在其臨界區執行。每個程序必須在進入其臨界區之前請求許可,實現此請求的程式碼部分是**入口區**,程式碼的末尾是**出口區**,其餘程式碼是**剩餘區**。
以下是特定程序 P1 臨界區的結構
臨界區必須滿足三個要求
- **互斥** - 如果一個程序,例如 P1,正在其臨界區執行,則任何其他程序,例如 P2,都不能在其臨界區執行。
- **進展** - 如果沒有程序在其臨界區執行,並且有一些程序想要進入其臨界區,則只有那些不在其剩餘區執行的程序才能請求進入臨界區,並且選擇不會無限期推遲。
- **有界等待** - 在有界等待中,對程序在發出進入其臨界區的請求後以及在該請求被授予之前可以進入其臨界區的次數有限制。
作業系統中常用兩種方法來處理臨界區。
**搶佔式核心** - 搶佔式核心允許程序在核心模式下執行時被搶佔。
**非搶佔式核心** - 非搶佔式核心不允許在核心模式下執行的程序被搶佔。
Peterson 解法
Peterson 解法是解決臨界區問題的經典基於軟體的解決方案。它僅限於兩個程序,它們在臨界區和剩餘區之間交替執行。Peterson 的解法需要兩個在兩個程序之間共享的資料項,即:
- Int turn;
- Boolean flag[2];
這裡,變數 turn 指示誰輪到進入其臨界區,flag 陣列指示程序是否準備好進入其臨界區。
如果 turn == i,則表示允許程序 Pi 進入其臨界區。
如果 flag[j] 為 TRUE,則表示程序 j 準備好進入其臨界區。
以下是 Peterson 解法中程序 P 的結構
Peterson 解法保留了所有三個條件:
- **互斥** - 每次只有一個程序可以訪問臨界區。
- **進展** - 臨界區外的程序不會阻止其他程序進入臨界區。
- **有界等待** - 每個程序都將有機會進入其臨界區,而不會無限期等待。
同步硬體
它使用兩種型別的指令實現:
- Test and Set()
- swap()
Test and Set() 是一種硬體解決方案,用於解決同步問題。其中,有一個由多個程序共享的共享變數,稱為 Lock,它可以取 0 和 1 這兩個值,其中 1 表示獲得鎖,0 表示釋放鎖。
每當程序嘗試進入其臨界區時,都需要查詢鎖的值。如果鎖的值為 1,則它們必須等待,直到鎖的值更改為 0。
以下是使用 TestAndSet() 實現的互斥。
訊號量
訊號量是一種同步工具,用於克服 TestAndSet() 和 Swap() 指令產生的問題。訊號量 S 是一個整數變數,可以透過兩個標準原子操作訪問,即 wait() 和 signal()
wait() 函式
wait(S) { While S <= 0 ; // no operation S--; }
Signal() 函式
signal(S) { S++; }
當一個程序修改訊號量的值時,任何其他程序都不能同時操作相同的訊號量值。
以下是使用訊號量實現的互斥。
作業系統使用兩種型別的訊號量:
**計數訊號量** - 此型別訊號量的值可以超過不受限制的域。
**二元訊號量** - 此型別訊號量的值可以在 0 和 1 之間變化。它們也稱為互斥鎖。作業系統利用它來解決多個程序中臨界區的問題。