📜  过程同步中的烘焙算法

📅  最后修改于: 2021-09-27 06:22:12             🧑  作者: Mango

先决条件——临界区、进程同步、进程间通信

Bakery 算法是 N 过程一般情况下互斥问题的最简单的已知解决方案之一。 Bakery 算法是 N 个过程的关键部分解决方案。该算法保留了先到先得的特性。

  • 在进入其临界区之前,该进程会收到一个数字。最小编号的持有者进入临界区。
  • 如果进程 Pi 和 Pj 收到相同的数字,
    if i < j 
    Pi is served first; 
    else 
    Pj is served first.
  • 编号方案总是以枚举的递增顺序生成数字;即 1, 2, 3, 3, 3, 3, 4, 5, …

符号 –字典顺序(票号,进程 ID #) – 首先比较票号。如果相同,则接下来比较进程 ID,即-

– (a, b) < (c, d) if a < c or if a = c and b < d
– max(a [0], . . ., a [n-1]) is a number, k, such that k >= a[i]  for i = 0, . . ., n - 1

共享数据——选择的是一个布尔值数组 [0..n – 1]; & number 是一个包含整数值的数组 [0..n – 1]。两者都分别初始化为False 和 Zero

算法伪代码 –

repeat
    choosing[i] := true;
    number[i] := max(number[0], number[1], ..., number[n - 1])+1;
    choosing[i] := false;
    for j := 0 to n - 1
        do begin
            while choosing[j] do no-op;
            while number[j] != 0
                and (number[j], j) < (number[i], i) do no-op;
        end;

        critical section
    
    number[i] := 0;
    
        remainder section

until false;

解释 –
首先,进程将其“选择”变量设置为 TRUE,表明其意图进入临界区。然后它被分配与其他进程对应的最高票号。然后将“choosing”变量设置为 FALSE 表示它现在有一个新的票号。这实际上是算法中最重要和最令人困惑的部分。

它本身就是一个小的临界区!前三行的真正目的是,如果一个进程正在修改其 TICKET 值,那么此时不应允许其他进程检查其现在已过时的旧票证值。这就是为什么在检查票证值之前的 for 循环中,我们首先确保所有其他进程的“选择”变量为 FALSE。

之后,我们继续检查具有最少票号/进程 ID 的进程进入临界区的进程的票证值。退出部分只是将票证值重置为零。

代码——这是烘焙算法的 C 代码实现。在 UNIX 环境中运行以下命令 –

// Importing the thread library
#include "pthread.h"
  
#include "stdio.h"
  
// Importing POSIX Operating System API library
#include "unistd.h"
  
#include "string.h"
  
// This is a memory barrier instruction.
// Causes compiler to enforce an ordering
// constraint on memory operations.
// This means that operations issued prior
// to the barrier will be performed
// before operations issued after the barrier.
#define MEMBAR __sync_synchronize()
#define THREAD_COUNT 8
  
volatile int tickets[THREAD_COUNT];
volatile int choosing[THREAD_COUNT];
  
// VOLATILE used to prevent the compiler
// from applying any optimizations.
volatile int resource;
  
void lock(int thread)
{
  
    // Before getting the ticket number
    //"choosing" variable is set to be true
    choosing[thread] = 1;
  
    MEMBAR;
    // Memory barrier applied
  
    int max_ticket = 0;
  
    // Finding Maximum ticket value among current threads
    for (int i = 0; i < THREAD_COUNT; ++i) {
  
        int ticket = tickets[i];
        max_ticket = ticket > max_ticket ? ticket : max_ticket;
    }
  
    // Allotting a new ticket value as MAXIMUM + 1
    tickets[thread] = max_ticket + 1;
  
    MEMBAR;
    choosing[thread] = 0;
    MEMBAR;
  
    // The ENTRY Section starts from here
    for (int other = 0; other < THREAD_COUNT; ++other) {
  
        // Applying the bakery algorithm conditions
        while (choosing[other]) {
        }
  
        MEMBAR;
  
        while (tickets[other] != 0 && (tickets[other]
                                           < tickets[thread]
                                       || (tickets[other]
                                               == tickets[thread]
                                           && other < thread))) {
        }
    }
}
  
// EXIT Section
void unlock(int thread)
{
  
    MEMBAR;
    tickets[thread] = 0;
}
  
// The CRITICAL Section
void use_resource(int thread)
{
  
    if (resource != 0) {
        printf("Resource was acquired by %d, but is still in-use by %d!\n",
               thread, resource);
    }
  
    resource = thread;
    printf("%d using resource...\n", thread);
  
    MEMBAR;
    sleep(2);
    resource = 0;
}
  
// A simplified function to show the implementation
void* thread_body(void* arg)
{
  
    long thread = (long)arg;
    lock(thread);
    use_resource(thread);
    unlock(thread);
    return NULL;
}
  
int main(int argc, char** argv)
{
  
    memset((void*)tickets, 0, sizeof(tickets));
    memset((void*)choosing, 0, sizeof(choosing));
    resource = 0;
  
    // Declaring the thread variables
    pthread_t threads[THREAD_COUNT];
  
    for (int i = 0; i < THREAD_COUNT; ++i) {
  
        // Creating a new thread with the function
        //"thread_body" as its thread routine
        pthread_create(&threads[i], NULL, &thread_body, (void*)((long)i));
    }
  
    for (int i = 0; i < THREAD_COUNT; ++i) {
  
        // Reaping the resources used by
        // all threads once their task is completed !
        pthread_join(threads[i], NULL);
    }
  
    return 0;
}

输出:
输出