📜  进程同步中的 Dekker 算法

📅  最后修改于: 2021-09-27 15:33:56             🧑  作者: Mango

先决条件——进程同步、进程间通信
为了获得这样的互斥、有界等待和进度,已经实现了几种算法,其中之一是 Dekker 算法。要理解算法,让我们先了解临界区问题的解决方案。
一个过程通常表示为:

do {
    //entry section
        critical section
    //exit section
        remainder section
} while (TRUE);

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

  1. 互斥
  2. 进步
  3. 有界等待

确保以上所有因素的解决方案之一是彼得森的解决方案。
另一个是Dekker 的解决方案。 Dekker 的算法是临界区问题的第一个可证明正确的解决方案。它允许两个线程在没有冲突的情况下共享一次性资源,仅使用共享内存进行通信。它避免了天真的轮换算法的严格交替,并且是最早发明的互斥算法之一。
尽管 Dekker 的解决方案有很多版本,但最终版本或第 5 版本是满足上述所有条件且效率最高的版本。
注 –此处提到的 Dekker 解决方案仅确保两个进程之间的互斥,通过正确使用数组和变量,它可以扩展到两个以上的进程。
算法——它需要一个布尔值数组和一个整数变量:

var flag: array [0..1] of boolean;
turn: 0..1;
repeat

        flag[i] := true;
        while flag[j] do
                if turn = j then
                begin
                        flag[i] := false;
                        while turn = j do no-op;
                        flag[i] := true;
                end;

                critical section

        turn := j;
        flag[i] := false;

                remainder section

until false;

Dekker 解决方案的第一个版本——这个想法是在进程之间使用公共或共享线程号,如果共享线程表明前一个进程已经在运行,则阻止另一个进程进入其临界区。

CPP
Main()
{
 
    int thread_number = 1;
    startThreads();
}
 
Thread1()
{
    do {
 
        // entry section
        // wait until threadnumber is 1
        while (threadnumber == 2)
            ;
 
        // critical section
 
        // exit section
        // give access to the other thread
        threadnumber = 2;
 
        // remainder section
 
    } while (completed == false)
}
 
Thread2()
{
 
    do {
 
        // entry section
        // wait until threadnumber is 2
        while (threadnumber == 1)
            ;
 
        // critical section
 
        // exit section
        // give access to the other thread
        threadnumber = 1;
 
        // remainder section
 
    } while (completed == false)
}


CPP
Main()
{
 
    // flags to indicate if each thread is in
    // its critical section or not.
    boolean thread1 = false;
    boolean thread2 = false;
 
    startThreads();
}
 
Thread1()
{
 
    do {
 
        // entry section
        // wait until thread2 is in its critical section
        while (thread2 == true)
            ;
 
        // indicate thread1 entering its critical section
        thread1 = true;
 
        // critical section
 
        // exit section
        // indicate thread1 exiting its critical section
        thread1 = false;
 
        // remainder section
 
    } while (completed == false)
}
 
Thread2()
{
 
    do {
 
        // entry section
        // wait until thread1 is in its critical section
        while (thread1 == true)
            ;
 
        // indicate thread2 entering its critical section
        thread2 = true;
 
        // critical section
 
        // exit section
        // indicate thread2 exiting its critical section
        thread2 = false;
 
        // remainder section
 
    } while (completed == false)
}


CPP
Main()
{
 
    // flags to indicate if each thread is in
    // queue to enter its critical section
    boolean thread1wantstoenter = false;
    boolean thread2wantstoenter = false;
 
    startThreads();
}
 
Thread1()
{
 
    do {
 
        thread1wantstoenter = true;
 
        // entry section
        // wait until thread2 wants to enter
        // its critical section
        while (thread2wantstoenter == true)
            ;
 
        // critical section
 
        // exit section
        // indicate thread1 has completed
        // its critical section
        thread1wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}
 
Thread2()
{
 
    do {
 
        thread2wantstoenter = true;
 
        // entry section
        // wait until thread1 wants to enter
        // its critical section
        while (thread1wantstoenter == true)
            ;
 
        // critical section
 
        // exit section
        // indicate thread2 has completed
        // its critical section
        thread2wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}


CPP
Main()
{
 
    // flags to indicate if each thread is in
    // queue to enter its critical section
    boolean thread1wantstoenter = false;
    boolean thread2wantstoenter = false;
 
    startThreads();
}
 
Thread1()
{
 
    do {
 
        thread1wantstoenter = true;
 
        while (thread2wantstoenter == true) {
 
            // gives access to other thread
            // wait for random amount of time
            thread1wantstoenter = false;
 
            thread1wantstoenter = true;
        }
 
        // entry section
        // wait until thread2 wants to enter
        // its critical section
 
        // critical section
 
        // exit section
        // indicate thread1 has completed
        // its critical section
        thread1wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}
 
Thread2()
{
 
    do {
 
        thread2wantstoenter = true;
 
        while (thread1wantstoenter == true) {
 
            // gives access to other thread
            // wait for random amount of time
            thread2wantstoenter = false;
 
            thread2wantstoenter = true;
        }
 
        // entry section
        // wait until thread1 wants to enter
        // its critical section
 
        // critical section
 
        // exit section
        // indicate thread2 has completed
        // its critical section
        thread2wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}


CPP
Main()
{
 
    // to denote which thread will enter next
    int favouredthread = 1;
 
    // flags to indicate if each thread is in
    // queue to enter its critical section
    boolean thread1wantstoenter = false;
    boolean thread2wantstoenter = false;
 
    startThreads();
}
 
Thread1()
{
    do {
 
        thread1wantstoenter = true;
 
        // entry section
        // wait until thread2 wants to enter
        // its critical section
        while (thread2wantstoenter == true) {
 
            // if 2nd thread is more favored
            if (favaouredthread == 2) {
 
                // gives access to other thread
                thread1wantstoenter = false;
 
                // wait until this thread is favored
                while (favouredthread == 2)
                    ;
 
                thread1wantstoenter = true;
            }
        }
 
        // critical section
 
        // favor the 2nd thread
        favouredthread = 2;
 
        // exit section
        // indicate thread1 has completed
        // its critical section
        thread1wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}
 
Thread2()
{
 
    do {
 
        thread2wantstoenter = true;
 
        // entry section
        // wait until thread1 wants to enter
        // its critical section
        while (thread1wantstoenter == true) {
 
            // if 1st thread is more favored
            if (favaouredthread == 1) {
 
                // gives access to other thread
                thread2wantstoenter = false;
 
                // wait until this thread is favored
                while (favouredthread == 1)
                    ;
 
                thread2wantstoenter = true;
            }
        }
 
        // critical section
 
        // favour the 1st thread
        favouredthread = 1;
 
        // exit section
        // indicate thread2 has completed
        // its critical section
        thread2wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}


上述实现中出现的问题是锁步同步,即每个线程的执行都依赖于另一个线程。如果其中一个进程完成,则第二个进程运行,访问已完成的进程并等待轮到它,但是,前一个进程已经完成并且永远不会运行以将访问权返回给后一个进程。因此,第二个进程会无限等待。
Dekker 解决方案的第二个版本——为了移除锁步同步,它使用两个标志来指示其当前状态,并在进入和退出部分相应地更新它们。

CPP

Main()
{
 
    // flags to indicate if each thread is in
    // its critical section or not.
    boolean thread1 = false;
    boolean thread2 = false;
 
    startThreads();
}
 
Thread1()
{
 
    do {
 
        // entry section
        // wait until thread2 is in its critical section
        while (thread2 == true)
            ;
 
        // indicate thread1 entering its critical section
        thread1 = true;
 
        // critical section
 
        // exit section
        // indicate thread1 exiting its critical section
        thread1 = false;
 
        // remainder section
 
    } while (completed == false)
}
 
Thread2()
{
 
    do {
 
        // entry section
        // wait until thread1 is in its critical section
        while (thread1 == true)
            ;
 
        // indicate thread2 entering its critical section
        thread2 = true;
 
        // critical section
 
        // exit section
        // indicate thread2 exiting its critical section
        thread2 = false;
 
        // remainder section
 
    } while (completed == false)
}

上述版本出现的问题是互斥本身。如果在标志更新期间(即在 current_thread = true 期间)线程被抢占(停止),那么一旦被抢占的线程重新启动,两个线程都会进入它们的临界区,同样可以在开始时观察到,当两个标志都为假时.
Dekker 解决方案的第三个版本——为了重新确保互斥,它在入口部分本身之前设置标志。

CPP

Main()
{
 
    // flags to indicate if each thread is in
    // queue to enter its critical section
    boolean thread1wantstoenter = false;
    boolean thread2wantstoenter = false;
 
    startThreads();
}
 
Thread1()
{
 
    do {
 
        thread1wantstoenter = true;
 
        // entry section
        // wait until thread2 wants to enter
        // its critical section
        while (thread2wantstoenter == true)
            ;
 
        // critical section
 
        // exit section
        // indicate thread1 has completed
        // its critical section
        thread1wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}
 
Thread2()
{
 
    do {
 
        thread2wantstoenter = true;
 
        // entry section
        // wait until thread1 wants to enter
        // its critical section
        while (thread1wantstoenter == true)
            ;
 
        // critical section
 
        // exit section
        // indicate thread2 has completed
        // its critical section
        thread2wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}

这个版本的问题是死锁的可能性。两个线程可以同时将它们的标志设置为真,并且以后都将无限等待。
Dekker 解决方案的第四个版本——使用小时间间隔重新检查条件,消除死锁并确保互斥。

CPP

Main()
{
 
    // flags to indicate if each thread is in
    // queue to enter its critical section
    boolean thread1wantstoenter = false;
    boolean thread2wantstoenter = false;
 
    startThreads();
}
 
Thread1()
{
 
    do {
 
        thread1wantstoenter = true;
 
        while (thread2wantstoenter == true) {
 
            // gives access to other thread
            // wait for random amount of time
            thread1wantstoenter = false;
 
            thread1wantstoenter = true;
        }
 
        // entry section
        // wait until thread2 wants to enter
        // its critical section
 
        // critical section
 
        // exit section
        // indicate thread1 has completed
        // its critical section
        thread1wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}
 
Thread2()
{
 
    do {
 
        thread2wantstoenter = true;
 
        while (thread1wantstoenter == true) {
 
            // gives access to other thread
            // wait for random amount of time
            thread2wantstoenter = false;
 
            thread2wantstoenter = true;
        }
 
        // entry section
        // wait until thread1 wants to enter
        // its critical section
 
        // critical section
 
        // exit section
        // indicate thread2 has completed
        // its critical section
        thread2wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}

这个版本的问题是无限期推迟。此外,随机时间量是不稳定的,具体取决于算法实施的情况,因此在关键业务系统中不是可接受的解决方案。
Dekker’s Algorithm : Final and completed 解决方案 –想法是使用偏好的线程概念来确定临界区的入口。优先线程在提供互斥和避免死锁、无限延迟或锁步同步的线程之间交替。

CPP

Main()
{
 
    // to denote which thread will enter next
    int favouredthread = 1;
 
    // flags to indicate if each thread is in
    // queue to enter its critical section
    boolean thread1wantstoenter = false;
    boolean thread2wantstoenter = false;
 
    startThreads();
}
 
Thread1()
{
    do {
 
        thread1wantstoenter = true;
 
        // entry section
        // wait until thread2 wants to enter
        // its critical section
        while (thread2wantstoenter == true) {
 
            // if 2nd thread is more favored
            if (favaouredthread == 2) {
 
                // gives access to other thread
                thread1wantstoenter = false;
 
                // wait until this thread is favored
                while (favouredthread == 2)
                    ;
 
                thread1wantstoenter = true;
            }
        }
 
        // critical section
 
        // favor the 2nd thread
        favouredthread = 2;
 
        // exit section
        // indicate thread1 has completed
        // its critical section
        thread1wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}
 
Thread2()
{
 
    do {
 
        thread2wantstoenter = true;
 
        // entry section
        // wait until thread1 wants to enter
        // its critical section
        while (thread1wantstoenter == true) {
 
            // if 1st thread is more favored
            if (favaouredthread == 1) {
 
                // gives access to other thread
                thread2wantstoenter = false;
 
                // wait until this thread is favored
                while (favouredthread == 1)
                    ;
 
                thread2wantstoenter = true;
            }
        }
 
        // critical section
 
        // favour the 1st thread
        favouredthread = 1;
 
        // exit section
        // indicate thread2 has completed
        // its critical section
        thread2wantstoenter = false;
 
        // remainder section
 
    } while (completed == false)
}

此版本保证了关键解决方案问题的完整解决方案。
参考 –
Dekker 算法 -csisdmz.ul.ie
Dekker 算法 – 维基百科