进程同步中的烘焙算法

先决条件—— 临界截面 , 进程同步 , 进程间通信

null

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

  • 在进入关键区域之前,流程会收到一个数字。人数最少的持有者进入临界区。
  • 如果进程Pi和Pj收到相同的数字,
    if i < j 
    Pi is served first; 
    else 
    Pj is served first.
  • 编号方案总是以递增的枚举顺序生成数字;i、 例如,1,2,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]。两者都初始化为 假&零 分别地

算法伪码-

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,表明其进入临界区的意图。然后,它会被分配与其他进程对应的最高票证号。然后,“Selecting”变量被设置为FALSE,表示它现在有了一个新的票号。事实上,这是算法中最重要、最令人困惑的部分。

这实际上是一个小的关键部分本身!前三行的目的是,如果一个进程正在修改其票证值,那么此时不应允许其他进程检查其已过时的旧票证值。这就是为什么在for循环中,在检查票证值之前,我们首先确保所有其他进程的“selection”变量都为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!" ,
thread , resource);
}
resource = thread ;
printf ( "%d using resource..." , 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;
}


输出: Output

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享