操作系统中的 Dekker 算法

operating systemwindowsmca更新于 2026/2/6 4:37:17

Dekker 算法

Dekker 算法是临界区问题的第一个解决方案。该算法有多个版本,第五个版本(即最后一个版本)满足以下所有条件,是所有版本中效率最高的。

临界区问题的解决必须满足以下三个条件:

  • 互斥
  • 进展
  • 有限等待

第一个版本

  • Dekker 算法成功实现了互斥。
  • 它使用变量来控制线程执行。
  • 它不断检查临界区是否可用。

示例

main(){
   int thread_no = 1;
   startThreads();
}
Thread1(){
   do {
      // 入口段
      // 等待直到 threadno 为 1
      while (threado == 2)
            ;
      // 临界段
      // 退出段
      // 允许访问其他线程
      threadno = 2;
      // 剩余段
   } while (completed == false)
}
Thread2(){
   do {
      // 入口段
      // 等待直到 threadno 为 2
      while (threadno == 1)
            ;
      // 临界段
      // 退出段
      // 授予其他线程访问权限
      threadno = 1;
      // 剩余段
   } while (completed == false)
}

问题

Dekker 算法第一版的问题在于锁步同步的实现。这意味着每个线程都依赖于其他线程来完成其执行。如果两个进程中的一个完成了执行,则第二个进程运行。然后,它将访问权限授予已完成的进程并等待其运行。但已完成的进程永远不会运行,因此它永远不会将访问权限交还给第二个进程。因此,第二个进程将无限期地等待。

第二版

在 Dekker 算法的第二版中,锁步同步被移除。它是通过使用两个标志来指示其当前状态,并在入口和出口部分相应地更新它们来实现的。

示例

main(){
   // 标志用于指示每个线程是否处于
   // 是否处于临界区。
   boolean th1 = false;
   boolean th2 = false;
   startThreads();
}
Thread1(){
   do {
      // 进入临界区
      // 等待 th2 进入临界区
      while (th2 == true);
      // 指示线程 1 进入临界区
      th1 = true;
      // 临界区
      // 退出部分
      // 指示 th1 退出其临界区
      th1 = false;
      // 剩余部分
   } while (completed == false)
}
Thread2(){
   do {
      // 进入部分
      // 等到 th1 进入其临界区
      while (th1 == true);
      // 指示 th2 进入其临界区
      th2 = true;
      // 临界区
      // 退出区
      // 指示 th2 退出其临界区
      th2 = false;
      // remainder section
   } while (completed == false)
}

问题

此版本违反了互斥原则。在标志更新期间,如果线程被抢占,则两个线程都会进入临界区。一旦被抢占的线程重新启动,在启动时,当两个标志都为 false 时,也会观察到同样的情况。

第三版

在此版本中,临界区标志在进入临界区测试之前设置,以确保互斥。

main(){
   // 标志用于指示每个线程是否在
   // 进入其临界区的队列
   boolean th1wantstoenter = false;
   boolean th2wantstoenter = false;
   startThreads();
}
Thread1(){
   do {
      th1wantstoenter = true;
      // 入口段
      // 等待直到 th2 想要进入
      // 其临界段
      while (th2wantstoenter == true)
             ;
      // 临界段
      // 出口段
      // 指示 th1 已完成
      // 其临界区
      th1wantstoenter = false;
      // 剩余区
   } while (completed == false)
}
Thread2(){
   do {
      th2wantstoenter = true;
      // 入口区
      // 等待 th1 想要进入
      // 其临界区
      while (th1wantstoenter == true)
               ;
      // 临界区
      // 退出区
      // 指示 th2 已完成
      // 其临界区
      th2wantstoenter = false;
      // 剩余区
   } while (completed == false)
}

问题

此版本未能解决互斥问题。它还引入了死锁的可能性,两个线程可能同时获得标志,并且它们将无限期地等待。

第四版

在此版本的 Dekker 算法中,它将标志设置为 false 一小段时间以提供控制,并解决了互斥和死锁问题。

示例

main(){
   // 标志,指示每个线程是否在
   // 进入其临界区的队列
   boolean th1wantstoenter = false;
   boolean th2wantstoenter = false;
   startThreads();
}
Thread1(){
   do {
      th1wantstoenter = true;
      while (th2wantstoenter == true) {
      // 允许访问其他线程
      // 等待随机时间
      th1wantstoenter = false;
      th1wantstoenter = true;
   }
   // 进入段
   // 等待直到 th2 想要进入
   // 其临界区
   // 临界区
   // 退出区
   // 指示 th1 已完成
   // 其临界区
   th1wantstoenter = false;
   // 剩余区
   } while (completed == false)
}
Thread2(){
   do {
      th2wantstoenter = true;
      while (th1wantstoenter == true) {
      // 允许访问其他线程
      // 等待随机时间
      th2wantstoenter = false;
      th2wantstoenter = true;
   }
   // 入口段
   // 等待直到 th1 想要进入
   // 其临界段
   // 临界段
   // 出口段
   // 指示 th2 已完成
   // 其临界段
   th2wantstoenter = false;
   // 剩余段
   } while (completed == false)
}

问题

此版本的问题在于无限期推迟。随机时间量是不可预测的,具体取决于算法的实现情况,因此在业务关键型系统中是不可接受的。

第五版(最终解决方案)

在此版本中,使用指定线程运动来确定进入临界区。它通过解决哪个线程应该先执行的冲突来提供互斥,并避免死锁、无限期推迟或锁步同步。此版本的 Dekker 算法提供了临界区问题的完整解决方案。

示例

main(){
   // 表示下一个进入的线程
   int favoredthread = 1;
   // 标志,指示每个线程是否处于
   // 进入临界区的队列
   boolean th1wantstoenter = false;
   boolean th2wantstoenter = false;
   startThreads();
}
Thread1(){
   do {
      thread1wantstoenter = true;
      // 进入临界区
      // 等待直到 th2 想要进入
      // 临界区
      while (th2wantstoenter == true) {
          // 如果第二个线程更受欢迎
          if (favouredthread == 2) {
            // 允许访问其他线程
            th1wantstoenter = false;
            // 等待直到此线程被优先访问
            while (favouredthread == 2);
            th1wantstoenter = true;
         }
      }
      // 临界区
      // 支持第二个线程
      favoredthread = 2;
      // 退出区
      // 指示 th1 已完成
      // 其临界区
      th1wantstoenter = false;
      // 剩余部分
   } while (completed == false)
}
Thread2(){
   do {
      th2wantstoenter = true;
      // 进入部分
      // 等待直到 th1 想要进入
      // 其临界区
      while (th1wantstoenter == true) {
          // 如果第一个线程更受欢迎
         if (favaouredthread == 1) {
              // 允许访问其他线程
            th2wantstoenter = false;
            // 等待此线程被优先访问
          while (favouredthread == 1);
            th2wantstoenter = true;
         }
      }
      // 临界区
      // 优先使用第一个线程
      favoredthread = 1;
      // 退出区
      // 指示 th2 已完成
      // 其临界区
      th2wantstoenter = false;
      // 剩余区
   } while (completed == false)
}


相关文章



关于我们 | 隐私条款 | 学习路线
返回顶部