进程互斥的软件实现
进程互斥的软件实现方法,以下是详细介绍:
几种软件互斥
单标志法
- 实现逻辑
- 在进入区,只进行“检查”操作,不执行“上锁”动作 。通过一个共享变量
turn
来记录当前允许进入临界区的进程。 - 在退出区,把临界区的使用权转交给另一个进程,相当于为另一进程“解锁”,同时给自己“上锁”。例如,若进程
P0
在退出临界区时,将turn
设为1
,表示现在允许P1
进入临界区。
- 在进入区,只进行“检查”操作,不执行“上锁”动作 。通过一个共享变量
- 主要问题:不遵循“空闲让进”原则。如果一个进程一直不进入临界区,即使临界区空闲,另一个进程也无法进入。比如
P1
一直不访问临界区,P0
退出后也不能再次进入,导致资源浪费。
双标志先检查法
- 实现逻辑
- 在进入区,先检查其他进程是否想进入临界区(通过检查标志位),然后再设置自己的标志位(“上锁” ),表明自己要进入临界区;在退出区则执行“解锁”操作,即把自己的标志位设为表示不进入临界区的状态。
- 主要问题:不遵循“忙则等待”原则 。由于“检查”和“上锁”不是原子操作,可能出现两个进程都在检查时发现对方不想进入,然后同时设置自己标志位进入临界区的情况,无法保证互斥。
双标志后检查法
- 实现逻辑
- 在进入区,先设置自己的标志位(“加锁” ),表明自己想进入临界区,然后再检查其他进程的标志位;在退出区进行“解锁”操作。
- 主要问题:不遵循“空闲让进”和“有限等待”原则,可能导致“饥饿” 。当两个进程都争着进入临界区时,可能会不断重复设置标志位和检查的过程,谁都无法进入临界区,造成进程长期等待,即“饥饿”现象。
Peterson算法
- 实现逻辑
- 在进入区,进程先“主动争取”进入临界区(设置自己的标志位表示想进入 ),然后“主动谦让”(设置一个变量表示愿意把机会让给对方 ),最后检查对方是否想进以及己方是否谦让,根据结果决定是否进入临界区。
- 主要问题:不遵循“让权等待”原则,会发生“忙等” 。进程在等待进入临界区时,会持续占用CPU资源不断检查条件,而不是释放CPU,造成CPU资源浪费。
代码示例
以下是分别用Java代码实现上述几种进程互斥软件方法的示例:
单标志法
class SingleFlagMethod {
// 记录允许进入临界区的进程编号,0表示P0,1表示P1
static int turn = 0;
static class Process extends Thread {
int id; // 进程编号,0或1
Process(int id) {
this.id = id;
}
@Override
public void run() {
while (true) {
// 进入区
while (turn != id) {
// 等待,不做操作
}
// 临界区
System.out.println("Process " + id + " is in critical section");
// 退出区
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();
}
}
双标志先检查法
class DoubleFlagPreCheckMethod {
// 标志数组,记录进程是否想进入临界区
static boolean[] flag = {false, false};
static class Process extends Thread {
int id; // 进程编号,0或1
Process(int id) {
this.id = id;
}
@Override
public void run() {
while (true) {
// 进入区
flag[id] = true;
while (flag[1 - id]) {
// 等待
}
// 临界区
System.out.println("Process " + id + " is in critical section");
// 退出区
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();
}
}
双标志后检查法
class DoubleFlagPostCheckMethod {
// 标志数组,记录进程是否想进入临界区
static boolean[] flag = {false, false};
static class Process extends Thread {
int id; // 进程编号,0或1
Process(int id) {
this.id = id;
}
@Override
public void run() {
while (true) {
// 进入区
flag[id] = true;
while (flag[1 - id]) {
flag[id] = false;
flag[id] = true;
}
// 临界区
System.out.println("Process " + id + " is in critical section");
// 退出区
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 {
// 标志数组,记录进程是否想进入临界区
static boolean[] flag = {false, false};
// 记录谦让的进程编号
static int turn;
static class Process extends Thread {
int id; // 进程编号,0或1
Process(int id) {
this.id = id;
}
@Override
public void run() {
while (true) {
// 进入区
flag[id] = true;
turn = 1 - id;
while (flag[1 - id] && turn == 1 - id) {
// 等待
}
// 临界区
System.out.println("Process " + id + " is in critical section");
// 退出区
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算法虽然在一定程度上解决了互斥问题,但存在忙等现象。