進(jìn)程互斥的軟件實(shí)現(xiàn)
進(jìn)程互斥的軟件實(shí)現(xiàn)方法,以下是詳細(xì)介紹:
幾種軟件互斥
單標(biāo)志法
- 實(shí)現(xiàn)邏輯
- 在進(jìn)入?yún)^(qū),只進(jìn)行“檢查”操作,不執(zhí)行“上鎖”動(dòng)作 。通過一個(gè)共享變量
turn
來記錄當(dāng)前允許進(jìn)入臨界區(qū)的進(jìn)程。 - 在退出區(qū),把臨界區(qū)的使用權(quán)轉(zhuǎn)交給另一個(gè)進(jìn)程,相當(dāng)于為另一進(jìn)程“解鎖”,同時(shí)給自己“上鎖”。例如,若進(jìn)程
P0
在退出臨界區(qū)時(shí),將turn
設(shè)為1
,表示現(xiàn)在允許P1
進(jìn)入臨界區(qū)。
- 在進(jìn)入?yún)^(qū),只進(jìn)行“檢查”操作,不執(zhí)行“上鎖”動(dòng)作 。通過一個(gè)共享變量
- 主要問題:不遵循“空閑讓進(jìn)”原則。如果一個(gè)進(jìn)程一直不進(jìn)入臨界區(qū),即使臨界區(qū)空閑,另一個(gè)進(jìn)程也無法進(jìn)入。比如
P1
一直不訪問臨界區(qū),P0
退出后也不能再次進(jìn)入,導(dǎo)致資源浪費(fèi)。
雙標(biāo)志先檢查法
- 實(shí)現(xiàn)邏輯
- 在進(jìn)入?yún)^(qū),先檢查其他進(jìn)程是否想進(jìn)入臨界區(qū)(通過檢查標(biāo)志位),然后再設(shè)置自己的標(biāo)志位(“上鎖” ),表明自己要進(jìn)入臨界區(qū);在退出區(qū)則執(zhí)行“解鎖”操作,即把自己的標(biāo)志位設(shè)為表示不進(jìn)入臨界區(qū)的狀態(tài)。
- 主要問題:不遵循“忙則等待”原則 。由于“檢查”和“上鎖”不是原子操作,可能出現(xiàn)兩個(gè)進(jìn)程都在檢查時(shí)發(fā)現(xiàn)對方不想進(jìn)入,然后同時(shí)設(shè)置自己標(biāo)志位進(jìn)入臨界區(qū)的情況,無法保證互斥。
雙標(biāo)志后檢查法
- 實(shí)現(xiàn)邏輯
- 在進(jìn)入?yún)^(qū),先設(shè)置自己的標(biāo)志位(“加鎖” ),表明自己想進(jìn)入臨界區(qū),然后再檢查其他進(jìn)程的標(biāo)志位;在退出區(qū)進(jìn)行“解鎖”操作。
- 主要問題:不遵循“空閑讓進(jìn)”和“有限等待”原則,可能導(dǎo)致“饑餓” 。當(dāng)兩個(gè)進(jìn)程都爭著進(jìn)入臨界區(qū)時(shí),可能會(huì)不斷重復(fù)設(shè)置標(biāo)志位和檢查的過程,誰都無法進(jìn)入臨界區(qū),造成進(jìn)程長期等待,即“饑餓”現(xiàn)象。
Peterson算法
- 實(shí)現(xiàn)邏輯
- 在進(jìn)入?yún)^(qū),進(jìn)程先“主動(dòng)爭取”進(jìn)入臨界區(qū)(設(shè)置自己的標(biāo)志位表示想進(jìn)入 ),然后“主動(dòng)謙讓”(設(shè)置一個(gè)變量表示愿意把機(jī)會(huì)讓給對方 ),最后檢查對方是否想進(jìn)以及己方是否謙讓,根據(jù)結(jié)果決定是否進(jìn)入臨界區(qū)。
- 主要問題:不遵循“讓權(quán)等待”原則,會(huì)發(fā)生“忙等” 。進(jìn)程在等待進(jìn)入臨界區(qū)時(shí),會(huì)持續(xù)占用CPU資源不斷檢查條件,而不是釋放CPU,造成CPU資源浪費(fèi)。
代碼示例
以下是分別用Java代碼實(shí)現(xiàn)上述幾種進(jìn)程互斥軟件方法的示例:
單標(biāo)志法
class SingleFlagMethod {
// 記錄允許進(jìn)入臨界區(qū)的進(jìn)程編號(hào),0表示P0,1表示P1
static int turn = 0;
static class Process extends Thread {
int id; // 進(jìn)程編號(hào),0或1
Process(int id) {
this.id = id;
}
@Override
public void run() {
while (true) {
// 進(jìn)入?yún)^(qū)
while (turn != id) {
// 等待,不做操作
}
// 臨界區(qū)
System.out.println("Process " + id + " is in critical section");
// 退出區(qū)
turn = 1 - id;
System.out.println("Process " + id + " leaves critical section");
}
}
}
public static void main(String[] args) {
Process p0 = new Process(0);
Process p1 = new Process(1);
p0.start();
p1.start();
}
}
雙標(biāo)志先檢查法
class DoubleFlagPreCheckMethod {
// 標(biāo)志數(shù)組,記錄進(jìn)程是否想進(jìn)入臨界區(qū)
static boolean[] flag = {false, false};
static class Process extends Thread {
int id; // 進(jìn)程編號(hào),0或1
Process(int id) {
this.id = id;
}
@Override
public void run() {
while (true) {
// 進(jìn)入?yún)^(qū)
flag[id] = true;
while (flag[1 - id]) {
// 等待
}
// 臨界區(qū)
System.out.println("Process " + id + " is in critical section");
// 退出區(qū)
flag[id] = false;
System.out.println("Process " + id + " leaves critical section");
}
}
}
public static void main(String[] args) {
Process p0 = new Process(0);
Process p1 = new Process(1);
p0.start();
p1.start();
}
}
雙標(biāo)志后檢查法
class DoubleFlagPostCheckMethod {
// 標(biāo)志數(shù)組,記錄進(jìn)程是否想進(jìn)入臨界區(qū)
static boolean[] flag = {false, false};
static class Process extends Thread {
int id; // 進(jìn)程編號(hào),0或1
Process(int id) {
this.id = id;
}
@Override
public void run() {
while (true) {
// 進(jìn)入?yún)^(qū)
flag[id] = true;
while (flag[1 - id]) {
flag[id] = false;
flag[id] = true;
}
// 臨界區(qū)
System.out.println("Process " + id + " is in critical section");
// 退出區(qū)
flag[id] = false;
System.out.println("Process " + id + " leaves critical section");
}
}
}
public static void main(String[] args) {
Process p0 = new Process(0);
Process p1 = new Process(1);
p0.start();
p1.start();
}
}
Peterson算法
class PetersonAlgorithm {
// 標(biāo)志數(shù)組,記錄進(jìn)程是否想進(jìn)入臨界區(qū)
static boolean[] flag = {false, false};
// 記錄謙讓的進(jìn)程編號(hào)
static int turn;
static class Process extends Thread {
int id; // 進(jìn)程編號(hào),0或1
Process(int id) {
this.id = id;
}
@Override
public void run() {
while (true) {
// 進(jìn)入?yún)^(qū)
flag[id] = true;
turn = 1 - id;
while (flag[1 - id] && turn == 1 - id) {
// 等待
}
// 臨界區(qū)
System.out.println("Process " + id + " is in critical section");
// 退出區(qū)
flag[id] = false;
System.out.println("Process " + id + " leaves critical section");
}
}
}
public static void main(String[] args) {
Process p0 = new Process(0);
Process p1 = new Process(1);
p0.start();
p1.start();
}
}
這些代碼示例中,每個(gè)方法都創(chuàng)建了兩個(gè)線程來模擬兩個(gè)進(jìn)程,通過不同的邏輯來實(shí)現(xiàn)進(jìn)程互斥。但正如前面所述,單標(biāo)志法、雙標(biāo)志先檢查法、雙標(biāo)志后檢查法存在各自的缺陷,Peterson算法雖然在一定程度上解決了互斥問題,但存在忙等現(xiàn)象。