Peterson 算法

358 阅读1分钟

Peterson算法是一个实现互斥锁的并发程序设计算法,可以控制两个进程访问一个共享的单用户资源而不发生访问冲突。

  • 假设编译器和硬件保证了顺序和可见性
  • 实现一段任意长代码的原子性
void f() {
  int i;
  for(i = 0; i < 1000000; i++)
  {
    lock(); // 保证顺序、原子性、可见性
    x++;    // 临界区
    unlock();
  }
}

而Peteson算法的实现如下:

int turn, x = 0, y = 0; 
void thread1() 
{ 
    x = 1; turn = T2; 
    while (y && turn == T2) ; 
    // critical section 
    x = 0;
} 

void thread2() 
{ 
    y = 1; turn = T1; 
    while (x && turn == T1) ; 
    // critical section 
    y = 0; 
}

例如,求和:

#include <threads.h>

int sum = 0;
int turn, x = 0, y = 0; 
const int T1 = 0;
const int T2 = 1;
const int MAXN = 1e7;
void thread1() 
{ 
    int i;
    for(i = 0; i < MAXN; i++)
    {
      x = 1; turn = T2; 
      while (y && turn == T2) ; 
      // critical section 
        sum++;
      // 
      x = 0;
    }

} 

void thread2() 
{ 
    int i;
    for(i = 0; i < MAXN; i++)
    {
      y = 1; turn = T1; 
      while (x && turn == T1) ; 
      // critical section 
        sum++;
      //
      y = 0; 
    }

}
void cal()
{
  printf("sum = %d\n", sum);
}
int main()
{
  create(thread1);
  create(thread2);
  join(cal);
}

实验结果如下: 【使用Perterson】

image.png 【不使用Perterson】

image.png

显然,虽然Perterson算法在实际机器上无法实现真正的互斥(因为实际机器无法保证顺序和可见性),但效果要远好于不加互斥算法的原始程序。

当然,我们可以使用内存屏障来保证顺序性和可见性:

mfence, lfence, sfence什么作用?

#define mb() 	asm volatile("mfence":::"memory")
#define rmb()	asm volatile("lfence":::"memory")
#define wmb()	asm volatile("sfence" ::: "memory")

就是保证内存访问的串行化,内部操作就是在一系列内存访问中添加若干延迟,保证此指令之后的内存访问发生在此指令之前的内存访问完成之后(不出现重叠)

lfence 读串行化

sfence 写串行化

mfence 读写都串行化

#include <threads.h>

#define FENCE asm volatile ("mfence")
int sum = 0;
int turn, x = 0, y = 0; 
const int T1 = 0;
const int T2 = 1;
const int MAXN = 1e7;
void thread1() 
{ 
    int i;
    for(i = 0; i < MAXN; i++)
    {
      x = 1; FENCE;
      turn = T2; FENCE;
      while (1) 
      {
        if(!y) break; FENCE;
        if(turn != T2) break; FENCE;
      } 
      // critical section 
        sum++;
      // 
      x = 0;
    }

} 

void thread2() 
{ 
    int i;
    for(i = 0; i < MAXN; i++)
    {
      y = 1; FENCE;
      turn = T1;  FENCE;
      while (1) 
      {
        if(!x) break; FENCE;
        if(turn != T1) break; FENCE;
      }; 
      // critical section 
        sum++;
      //
      y = 0; 
    }

}
void cal()
{
  printf("sum = %d\n", sum);
}
int main()
{
  create(thread1);
  create(thread2);
  join(cal);
}

结果如下:

image.png