【操作系统】互斥:软件解决方法
互斥:軟件解決方法
算法一
算法思路
預(yù)留一個全局內(nèi)存區(qū)域,并標記為turn。進程(P0或P1)想進入它的臨界區(qū)執(zhí)行時,要先檢查turn的內(nèi)容。若turn的值等于進程號,則該進程可以進入它的臨界區(qū);否則該進程被強制等待。等待進程重復(fù)地讀取turn的值。直到被允許進入臨界區(qū)。這一過程稱為忙等待(busy waiting) 或 自旋等待(spin waiting),進程在獲得臨界區(qū)的訪問權(quán)并完成訪問后,必須為另一個進程更新turn的值。
P0 :
while(turn != 0) {/* busy waiting */} /* critical section */ turn = 1;P1 :
while(turn != 1) {/* busy waiting */} /* critical section */ turn = 0;缺陷
例如:若P0在1小時內(nèi)僅使用臨界區(qū)1次,而P1要以1000次/小時的速率使用臨界區(qū),則P1就必須適應(yīng)P0的節(jié)奏。
BUG
例如:P0在執(zhí)行到第二行代碼(臨界區(qū)內(nèi))時終止,此時turn值為0且此后不會被更新,因此P1將永久處于忙等待狀態(tài);P0在執(zhí)行到第三行代碼(臨界區(qū)外)時終止,此時turn值為1,于是P1進入臨界區(qū),執(zhí)行完后便將turn值置為0,而P0進程因為終止便不會再更新turn值,因此P1將永久處于忙等待狀態(tài)。
算法二
算法一僅由一個共享的仲裁變量去實現(xiàn)進程間的互斥,因此進程之間必須嚴格交替執(zhí)行,進程之間的依賴關(guān)系過強
算法二為每一個進程都配備了專門的”鑰匙“,這里定義一個bool數(shù)組flag,flag[0]與P0關(guān)聯(lián),flag[1]與P1關(guān)聯(lián),每個進程可檢查但不能改變另一個進程的flag值
算法思路
一個進程要進入臨界區(qū)時,它會周期性地檢查另一個進程的flag,直到其值為false,這表明另一個進程不在臨界區(qū)內(nèi)。檢查進程立即設(shè)置自己的flag為true,進入自己的臨界區(qū)。離開臨界區(qū)時,將自己的flag設(shè)置為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在執(zhí)行完第4行代碼后終止,此后flag[1]將一直保持false,因此P0將永遠不會進入忙等待狀態(tài)即說明其不會被阻塞
BUG
例如:P1執(zhí)行完第4行代碼后,于是P0跳出忙等待狀態(tài),但P0還未來得及執(zhí)行第二行代碼(將flag[0]設(shè)置為true)P1便又進入了臨界區(qū),隨后P0也進入臨界區(qū),便產(chǎn)生了訪問沖突
下面采用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;}} }兩個線程任務(wù)是,將公共變量var賦值為本線程對應(yīng)正確的值var_right,并輸出當前var的值,若兩個線程保證互斥則var總是等于var_right(Condition ‘var == var_right’ is always ‘true’)即不會輸出##
運行結(jié)果如下:
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可見采用算法二兩個線程之間發(fā)生了不少次的訪問沖突(輸出##)
算法三
算法二由于執(zhí)行速率不匹配的原因,導(dǎo)致兩個進程同時處在臨界區(qū)的位置,所以算法二的方案失敗
算法三通過簡單地交換兩條語句來解決這一問題
算法思路
將控制其他進程持續(xù)忙等待的語句提到了檢查flag值之前,確保了同一時刻只有一個進程進入臨界區(qū)
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執(zhí)行完第一行代碼后還未來得及執(zhí)行while語句,P1也執(zhí)行完了第一行代碼,此時flag[0]與flag[1]的值都為true,因此兩個進程都無法跳出忙等待的狀態(tài),從而造成死鎖
下面采用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;}} }運行結(jié)果如下:
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可見結(jié)果只被正確輸出了一次,此后兩線程皆處在忙等待狀態(tài),形成死鎖
算法四
算法三中,一個進程在設(shè)置其狀態(tài)時是不知道另一個進程的狀態(tài)的。由于每一個進程堅持要進入臨界區(qū),導(dǎo)致死鎖發(fā)生
算法四在算法三的基礎(chǔ)上引入**“謙讓”**的機制,在忙等待中隨時重設(shè)flag,一定程度上避免了死鎖發(fā)生
算法思路
當出現(xiàn)死鎖情況時(兩者都進入忙等待狀態(tài)),P0進程將flag[0]置為false并在該狀態(tài)延遲1秒,P1便可在這1秒之內(nèi)跳出忙等待狀態(tài),從而避免了死鎖的情況
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 算法
算法四可能會出現(xiàn) “互相謙讓” 的情況
Dekker算法通過turn變量表示哪個進程有權(quán)進入它的臨界區(qū),避免了**“互相謙讓”**的情況
算法思路
當P0要進入它的臨界區(qū)時,將其flag設(shè)置為true,然后檢查P1的flag。若為false,P0可以立即進入它的臨界區(qū);否則,P0要檢查turn,若發(fā)現(xiàn)turn為0,則P0要持續(xù)周期性地檢查P1的flag。而P1需要延期執(zhí)行并將flag設(shè)置為false,讓P0執(zhí)行。P0完成臨界區(qū)執(zhí)行后,將其flag設(shè)置為false以釋放臨界區(qū),并將turn設(shè)置為1,把權(quán)力轉(zhuǎn)交給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 算法解決了互斥問題,但復(fù)雜的程序很難實現(xiàn)且其正確性也很難證明。Peterson提出了一個簡單且精致的算法。和前面一樣,全局數(shù)組變量flag表明每個互斥進程的位置,全局變量turn解決同時發(fā)生的沖突。
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; }最后兩種正確算法相關(guān)內(nèi)容(驗證、說明等)待更新… …
總結(jié)
以上是生活随笔為你收集整理的【操作系统】互斥:软件解决方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小小蚁国最强区域战活动如何过关
- 下一篇: java,jdk安装,配置环境变量,wi