多线程互斥锁访问算法(上)------Peterson算法

你好!这里是风筝的博客,

欢迎和我一起交流。

在多线程存在的环境中,除了堆栈中的临时数据之外,所有的数据都是共享的。如果我们需要线程之间正确地运行,那么务必需要保证公共数据的执行和计算是正确的。简单一点说,就是保证数据在执行的时候必须是互斥的。否则,如果两个或者多个线程在同一时刻对数据进行了操作,那么后果是不可想象的。

那么,该怎么去实现一个资源(数据)的互斥访问呢?
那就是这章所讲的Peterson算法,它可以控制两个线程访问一个共享的单用户资源而不发生访问冲突。Gary L. Peterson于1981年提出此算法 。
我们看下代码实现:

#define true    1
#define false   0
int lock;
int hold [2]={false};

void enter_lock(int thread_id)
{
    hold[thread_id]=true;/*标记占有资源*/
    lock=thread_id;
    while(lock==thread_id&&(hold[1-thread_id]==true));/*阻塞,等待调度*/
}

void exit_lock(int thread_id)
{
    hold[thread_id]=false;/*标记释放资源*/
}

这就是Peterson算法的核心代码,当我们需要访问临界区(互斥资源)时:

void process_0(void)//线程0
{
    enter_lock(0);
    //临界区
    //访问资源
    exit_lock(0);
}
void process_1(void)//线程1
{
    enter_lock(1);
    //临界区
    //访问资源
    exit_lock(1);
}

我们可以来模拟一下:
当有两个线程(线程A,thread_id=0、线程B,thread_id=1)来抢占资源时,

我们可以假设三种情况来模拟:

一:当线程B不占有资源但是线程A想占有资源时,
线程A的enter_lock函数while里&&右边条件不成立,所以while不成立,线程A成功占有资源。

二:当线程B正在占有资源,但是系统发生调度使得线程A也想占有资源时,
线程A的enter_lock函数里while成立,使得线程A函数阻塞,
待线程B调用exit_lock函数后才会使得线程A里的while得以向下执行。

三:当线程B运行:”lock=thread_id;”语句之后,系统发生调用使得线程A也想占有资源时,
线程A的enter_lock函数里while成立,使得线程A函数阻塞,当系统调度使得线程B继续运行时,
因为全局变量lock被线程A更改了,所以线程B中while不成立,线程B成功占有资源,待线程B释放资源后,
线程A中while右半部不成立,所以也使得线程A占有资源。

显然易见,Peterson算法有个局限性,他只支持两个线程,如果在多一个线程,则会出现错误。
为了解决这个问题,下一章会讲另一种算法:多线程互斥锁访问算法(下)——Lamport算法(面包店算法)

Peterson算法不需要原子(atomic)操作,它是纯软件途径解决了互斥锁的实现。但需要注意限制CPU对内存的访问顺序的优化改变(编译器的一个优化点)。因为它依赖于这个时序实现:
先写入hold[thread_id]=true;再写入lock=thread_id;,否则会出现死锁。

你可能感兴趣的:(数据结构与算法)