【操作系统】互斥:软件解决方法
互斥:軟件解決方法
算法一
算法思路
預留一個全局內存區域,并標記為turn。進程(P0或P1)想進入它的臨界區執行時,要先檢查turn的內容。若turn的值等于進程號,則該進程可以進入它的臨界區;否則該進程被強制等待。等待進程重復地讀取turn的值。直到被允許進入臨界區。這一過程稱為忙等待(busy waiting) 或 自旋等待(spin waiting),進程在獲得臨界區的訪問權并完成訪問后,必須為另一個進程更新turn的值。
P0 :
while(turn != 0) {/* busy waiting */} /* critical section */ turn = 1;P1 :
while(turn != 1) {/* busy waiting */} /* critical section */ turn = 0;缺陷
例如:若P0在1小時內僅使用臨界區1次,而P1要以1000次/小時的速率使用臨界區,則P1就必須適應P0的節奏。
BUG
例如:P0在執行到第二行代碼(臨界區內)時終止,此時turn值為0且此后不會被更新,因此P1將永久處于忙等待狀態;P0在執行到第三行代碼(臨界區外)時終止,此時turn值為1,于是P1進入臨界區,執行完后便將turn值置為0,而P0進程因為終止便不會再更新turn值,因此P1將永久處于忙等待狀態。
算法二
算法一僅由一個共享的仲裁變量去實現進程間的互斥,因此進程之間必須嚴格交替執行,進程之間的依賴關系過強
算法二為每一個進程都配備了專門的”鑰匙“,這里定義一個bool數組flag,flag[0]與P0關聯,flag[1]與P1關聯,每個進程可檢查但不能改變另一個進程的flag值
算法思路
一個進程要進入臨界區時,它會周期性地檢查另一個進程的flag,直到其值為false,這表明另一個進程不在臨界區內。檢查進程立即設置自己的flag為true,進入自己的臨界區。離開臨界區時,將自己的flag設置為false。
P0 :
while(flag[1]) {/* busy waiting */} flag[0] = true; /* critical section */ flag[0] = false;P1 :
while(flag[0]) {/* busy waiting */} flag[1] = true; /* critical section */ flag[1] = false;進步
例如:當P1在執行完第4行代碼后終止,此后flag[1]將一直保持false,因此P0將永遠不會進入忙等待狀態即說明其不會被阻塞
BUG
例如:P1執行完第4行代碼后,于是P0跳出忙等待狀態,但P0還未來得及執行第二行代碼(將flag[0]設置為true)P1便又進入了臨界區,隨后P0也進入臨界區,便產生了訪問沖突
下面采用Java多線程編程模擬算法二并驗證BUG-2
package TEST;public class Multithreading {public static void main(String[] args) {Process p0 = new Process("P0", 0, 0);Process p1 = new Process("P1", 1, 1);p0.start();p1.start();} } class Process extends Thread {private static int var;private int var_right;private static boolean[] flag = new boolean[2];private String name;private int cnt_test = 100;private int No;private int No_other;public Process(String name, int No, int var_right) {super();this.name = name;this.No = No;this.var_right = var_right;this.No_other = (No + 1) % 2;}public void run() {for(int i = 0; i < cnt_test; i++) {while(flag[No_other]) { /* busy waiting */ }flag[No] = true;/*--- critical section begin---*/var = var_right;System.out.println(this.name + ":" + (var == var_right? var : "##"));/*--- critical section end---*/flag[No] = false;}} }兩個線程任務是,將公共變量var賦值為本線程對應正確的值var_right,并輸出當前var的值,若兩個線程保證互斥則var總是等于var_right(Condition ‘var == var_right’ is always ‘true’)即不會輸出##
運行結果如下:
P0:0 P1:## P0:0 P1:1 P0:0 P1:## P0:0 P1:## P0:0 P0:0 P0:0 P1:## P1:## P0:0 P1:1 P1:1 P0:## P1:1 P1:1 P1:1 P0:## P0:## P1:1 P0:0 P1:1 P1:## P0:0 P1:## P0:0 P1:1 P1:## P0:0 P1:1 P0:## P1:1 P0:## P1:1 P1:## P0:0 P0:0 P1:## P0:0 P1:1 P1:## P0:0 P0:0 P1:## P0:0 P1:1 P1:## P0:0 P1:## P0:0 P1:1 P1:## P0:0 P1:## P0:0 P1:1 P0:0 P1:1 P1:## P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P1:1 P0:0 P1:1 P0:0 P0:0 P1:1 P1:1 P1:1 P0:0 P1:## P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:## P0:0 P1:1 P1:## P0:0 P1:1 P1:## P0:0 P1:1 P1:## P0:0 P1:1 P1:1 P1:## P0:0 P1:1 P1:1 P0:0 P1:## P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:## P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P1:1 P0:## P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:1 P1:## P0:0 P1:1 P1:## P0:0 P1:1 P1:1 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0 P0:0Process finished with exit code 0可見采用算法二兩個線程之間發生了不少次的訪問沖突(輸出##)
算法三
算法二由于執行速率不匹配的原因,導致兩個進程同時處在臨界區的位置,所以算法二的方案失敗
算法三通過簡單地交換兩條語句來解決這一問題
算法思路
將控制其他進程持續忙等待的語句提到了檢查flag值之前,確保了同一時刻只有一個進程進入臨界區
P0 :
flag[0] = true; while(flag[1]) {/* busy waiting */} /* critical section */ flag[0] = false;P1 :
flag[1] = true; while(flag[0]) {/* busy waiting */} /* critical section */ flag[1] = false;進步
BUG
例如:P0執行完第一行代碼后還未來得及執行while語句,P1也執行完了第一行代碼,此時flag[0]與flag[1]的值都為true,因此兩個進程都無法跳出忙等待的狀態,從而造成死鎖
下面采用Java多線程編程模擬算法三并驗證BUG-2
package TEST;public class Multithreading {public static void main(String[] args) {Process p0 = new Process("P0", 0, 0);Process p1 = new Process("P1", 1, 1);p0.start();p1.start();} } class Process extends Thread {private static int cnt_busy_wait = 0;private static int var;private int var_right;private static boolean[] flag = new boolean[2];private String name;private int cnt_test = 100;private int No;private int No_other;public Process(String name, int No, int var_right) {super();this.name = name;this.No = No;this.var_right = var_right;this.No_other = (No + 1) % 2;}public void run() {for(int i = 0; i < cnt_test; i++) {flag[No] = true;while(flag[No_other]) {/* busy waiting */System.out.println(this.name + " BusyWaiting: " + (++cnt_busy_wait));if(cnt_busy_wait > 999) return;}/*--- critical section begin---*/var = var_right;System.out.println(this.name + ":" + (var == var_right? var : "##"));/*--- critical section end---*/flag[No] = false;}} }運行結果如下:
P0:0 P1 BusyWaiting: 1 P1 BusyWaiting: 3 P1 BusyWaiting: 4 P1 BusyWaiting: 5 P0 BusyWaiting: 2 P0 BusyWaiting: 7 P0 BusyWaiting: 8 P0 BusyWaiting: 9 P0 BusyWaiting: 10 P1 BusyWaiting: 6 P1 BusyWaiting: 12 P1 BusyWaiting: 13 P0 BusyWaiting: 11... ...P1 BusyWaiting: 995 P1 BusyWaiting: 996 P1 BusyWaiting: 997 P1 BusyWaiting: 998 P1 BusyWaiting: 999 P1 BusyWaiting: 1000 P0 BusyWaiting: 967Process finished with exit code 0可見結果只被正確輸出了一次,此后兩線程皆處在忙等待狀態,形成死鎖
算法四
算法三中,一個進程在設置其狀態時是不知道另一個進程的狀態的。由于每一個進程堅持要進入臨界區,導致死鎖發生
算法四在算法三的基礎上引入**“謙讓”**的機制,在忙等待中隨時重設flag,一定程度上避免了死鎖發生
算法思路
當出現死鎖情況時(兩者都進入忙等待狀態),P0進程將flag[0]置為false并在該狀態延遲1秒,P1便可在這1秒之內跳出忙等待狀態,從而避免了死鎖的情況
P0 :
flag[0] = true; while(flag[1]) {flag[0] = false;delay(1); // dalay 1 secflag[0] = true; } /* critical section */ flag[0] = false;P1 :
flag[1] = true; while(flag[0]) {flag[1] = false;delay(1); // dalay 1 secflag[1] = true; } /* critical section */ flag[1] = false;進步
缺陷
Dekker 算法
算法四可能會出現 “互相謙讓” 的情況
Dekker算法通過turn變量表示哪個進程有權進入它的臨界區,避免了**“互相謙讓”**的情況
算法思路
當P0要進入它的臨界區時,將其flag設置為true,然后檢查P1的flag。若為false,P0可以立即進入它的臨界區;否則,P0要檢查turn,若發現turn為0,則P0要持續周期性地檢查P1的flag。而P1需要延期執行并將flag設置為false,讓P0執行。P0完成臨界區執行后,將其flag設置為false以釋放臨界區,并將turn設置為1,把權力轉交給P1
bool flag[2]; int turn; void P0() {while (true){flag[0] = true;while (flag[1]){if (turn == 1){flag[0] = false;while(turn == 1) { /* busy waiting */ }flag[0] = true;}}/* critical section */turn = 1;flag[0] = false;} } void P1() {while (true){flag[1] = true;while (flag[0]){if (turn == 0){flag[1] = false;while (turn == 0) { /* busy waiting */ }flag[1] = true;}}/* critical section */turn = 0;flag[1] = false;} } int main() {flag[0] = false;flag[1] = false;turn = 1;parbegin(P0, P1); // concurrent executionreturn 0; }Peterson 算法
Dekker 算法解決了互斥問題,但復雜的程序很難實現且其正確性也很難證明。Peterson提出了一個簡單且精致的算法。和前面一樣,全局數組變量flag表明每個互斥進程的位置,全局變量turn解決同時發生的沖突。
bool flag[2]; int turn; void P0() {while(true){flag[0] = true;turn = 1;while(flag[1] && turn == 1) {/* busy waiting */}/* critical section */flag[0] = false;} } void P1() {while(true){flag[1] = true;turn = 0;while(flag[0] && turn == 0) {/* busy waiting */}/* critical section */flag[1] = false;} } int main() {flag[0] = false;flag[1] = false;parbegin(P0, P1); // concurrent executionreturn 0; }最后兩種正確算法相關內容(驗證、說明等)待更新… …
總結
以上是生活随笔為你收集整理的【操作系统】互斥:软件解决方法的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 小小蚁国最强区域战活动如何过关
- 下一篇: 四核cpu多少钱啊?
