双标志先检查法(Two-Flag Algorithm)

定义:

双标志先检查法(Two-Flag Algorithm)是一种解决进程互斥的算法,它是基于两个共享变量(标志)来保证两个进程之间的互斥访问。与单标志法相比,双标志法通过使用两个标志变量来避免单一标志法中的忙等待问题,并进一步提高互斥的效率和公平性。

原理:

双标志法的核心思想是使用两个共享标志(通常是 flag[0] 和 flag[1]),每个进程都有一个标志位,用来指示该进程是否准备进入临界区。当一个进程希望进入临界区时,它首先将自己的标志设置为 true,然后检查另一个进程的标志。如果另一个进程也希望进入临界区,它必须等待。双标志法通过双重检查机制,确保只有一个进程能够进入临界区。

双标记先检查法 (Double Flag, Check First):

A 标志 flag[0] ,B 标志 flag[1] 

  • 核心思想:先看对方想不想进,我再决定自己想不想进
  • 逻辑流程(以进程A为例):
    1. while(flag[1]==1); // 先检查对方是否在临界区或想进入临界区
    2. flag[0]=true;// 确认对方不在,自己再举手
    3. 访问临界区...
    4. flag[0]=false;// 访问完毕,放下手
  • 缺点:违背“忙则等待”原则。
    1. 如果A检查完  flag[1]为false 后,发生进程切换;
    2. B也检查完 flag[0]为false,接着切换回来。
    3. A设置 flag[0]=true 进入,B设置flag[1]=true进入。
    4. 两者会同时进入临界区,互斥失败。

双标志先检查法(Two-Flag Algorithm)

  • 核心思想:我先举手表示想进,然后再看对方想不想进。如果对方也想进,我就等一下
  • 逻辑流程(以进程A为例):
    1. flag[0]=true;// 先举手,表示我想进
    2. while(flag[1] == true);// 再检查对方是否也举手了
    3. 访问临界区...
    4. flag[0]=false;// 访问完毕,放下手
  • 缺点:违背“空闲让进” 和 “有限等待” 原则,可能导致死锁(饥饿)。如果A和B几乎同时执行了第1步(都举手了),然后互相在第2步检查对方,发现对方都举手了,于是双方都在死循环里等待对方先放下手,导致谁也无法进入临界区。

双标志先检查法的操作步骤:

假设有两个进程 A 和 B,它们共享两个标志位 flag[A] 和 flag[B],初始值均为 false。

进程 A 请求进入临界区:

  1. 先检查:进程 A 首先检查进程 B 的标志 flag[B]。

  2. 判断:

    • 如果 flag[B] 为 false(表示 B 不想进入临界区),则 A 继续下一步。

    • 如果 flag[B] 为 true(表示 B 正在或想要进入临界区),则 A 必须一直循环等待(忙等),直到 flag[B] 变为 false。

  3. 后修改:当确认 flag[B] 为 false 后,A 将自己的标志 flag[A] 设置为 true,表示自己现在要进入临界区。

  4. 进入:A 进入临界区执行代码。

进程 B 请求进入临界区:

  1. 先检查:同样地,进程 B 首先检查进程 A 的标志 flag[A]。

  2. 判断:

    • 如果 flag[A] 为 false,则 B 继续下一步。

    • 如果 flag[A] 为 true,则 B 必须一直循环等待,直到 flag[A] 变为 false。

  3. 后修改:当确认 flag[A] 为 false 后,B 将自己的标志 flag[B] 设置为 true。

  4. 进入:B 进入临界区执行代码。

3. 离开临界区(释放):

  1. 进程完成临界区任务后,将自己的标志位设置为 false(例如 A 将 flag[A] 设为 false)。

  2. 这表示它已释放临界区,允许另一个进程(B)检查到 false 后进入。

双标记先检查法伪代码示例:

// 进程 A 的代码
while (true) {
    // 1. 先检查对方是否想进
    while (flag[B] == true); 
    
    // 2. 确认对方不想进后,再修改自己的标志
    flag[A] = true;          
    
    // 3. 访问临界区
    critical_section();
    
    // 4. 退出区:释放锁
    flag[A] = false;         
    
    // 5. 剩余区
    remainder_section();
}

(同理,进程 B 的代码只需将上述代码中的 A 和 B 互换即可)

双标记法的工作原理:

  • 设置两个标志变量 flag[A] 和 flag[B],初始值均为 false。用于表示两个进程是否想要进入临界区。

  • 进程在进入临界区之前,必须先检查另一个进程的标志状态。只有当另一个进程的标志为 false(不想进入)时,当前进程才能继续。

  • 确认对方不想进入后,当前进程将自己的标志设置为 true,然后进入临界区。

  • 进程离开临界区时,将自己的标志设置为 false,允许另一个进程进入。

双标记法的优势:

避免忙等待:

  • 与单标志法相比,双标志法能有效减少忙等待。通过检查两个标志,进程能够在必要时进行等待,从而提高效率。

避免竞态条件:

  • 双标志法避免了竞态条件,即多个进程同时访问共享资源时产生的冲突,因为它保证在任何时刻只有一个进程可以进入临界区。

公平性:

  • 双标志法通过两次检查确保了公平性。两个进程都在检查对方的标志,并以此决定是否进入临界区,从而避免了进程饥饿(starvation)问题。

双标记法的缺点:

忙等待问题:

  • 双标志法仍然存在忙等待的问题。当一个进程等待时,它会不断检查标志变量的状态,浪费 CPU 时间,尤其是在进程数量多、临界区访问时间较长时,这种方式可能导致系统效率低下。

不能扩展到多个进程:

  • 双标志法设计时是为两个进程而设计的。如果需要多个进程共享资源,双标志法无法直接扩展,必须使用更复杂的同步机制(如信号量、互斥锁等)。

缺乏内存和时间的优化:

  • 双标志法需要两个共享标志变量,这在某些场景下可能不是最优的解决方案,尤其是当系统资源有限时。

双标记先检查法的改进:

增加时间限制或让进程睡眠:

  • 为了避免忙等待带来的性能问题,可以在双标志法中引入时间限制或让进程在等待时进入睡眠状态,减少不必要的 CPU 占用。

采用信号量或互斥锁:

  • 在多进程或多线程环境中,使用信号量(semaphore)或互斥锁(mutex)来代替双标志法,能够更高效地管理资源的访问,并且避免忙等待、进程饥饿等问题。

总结

双标志先检查法试图通过两个标志位来控制两个进程的互斥。它虽然改善了单标志法必须轮流执行的限制,但由于检查和修改标志位的操作无法保证原子性,它违背了“忙则等待”原则,存在严重的竞态条件,无法真正实现进程互斥。此外,它依然存在忙等待的缺陷,且仅适用于进程较少的简单场景。在现代复杂的操作系统中,通常会选择更高级的同步机制(如信号量、互斥锁等)来实现进程同步。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐