一、单标志法
核心思想
使用一个布尔变量(如`turn`)表示当前允许进入临界区的进程号。 - 若`turn == 当前进程号`,则允许进入临界区;
- 否则,进程需等待。
实现步骤
- 初始化`turn = 0`(或任意进程号);
- 进程进入临界区前检查`turn == 进程号`;
- 进入后执行临界区操作,最后将`turn`置为1。
示例代码
```c
int turn = 0; // 表示当前允许进入临界区的进程号
void P0() {
while (turn != 0); // P0等待
critical_section(); // 进入临界区
turn = 1; // 释放权限
}
void P1() {
while (turn != 1); // P1等待
critical_section(); // 进入临界区
turn = 0; // 释放权限
}
```
二、双标志先检查法(Peterson算法)
核心思想
使用两个布尔变量(如`flag`和`flag`)分别表示两个进程是否想进入临界区。 - 进程先检查对方是否不想进入(`!flag[对方]`);
- 若对方不想进入,则设置自身标志为`true`并尝试进入临界区。
实现步骤
- 初始化`flag = false`,`flag = false`;
- 进程进入临界区前检查`!flag[对方] && !flag[自身]`;
- 进入后执行临界区操作,最后将自身标志设为`false`。
示例代码
```c
bool flag = {false, false}; // flag表示P0,flag表示P1
void P0() {
while (flag || (flag && turn == 1)); // P0等待
flag = true; // 标记想进入临界区
critical_section(); // 进入临界区
flag = false; // 释放权限
}
void P1() {
while (flag || (flag && turn == 1)); // P1等待
flag = true; // 标记想进入临界区
critical_section(); // 进入临界区
flag = false; // 释放权限
}
```
三、其他注意事项
局限性
- 单标志法可能导致饥饿现象(如P0永远无法进入临界区);
- 双标志先检查法需要额外内存,且仍可能引发饥饿。
改进方案
- Peterson算法通过双方协商避免饥饿,但仅适用于两个进程;
- 软件解决方案中, 信号量(如二进制信号量)是更通用的方法,但需操作系统支持。
总结
进程互斥的软件实现需平衡简单性与安全性。单标志法和双标志先检查法适用于教学和简单场景,而Peterson算法在理论上更优。实际应用中,操作系统提供的同步机制(如互斥锁)通常更高效且可靠。