关于进程互斥-Peterson(皮特森)算法的讨论

首先我们用c++实现一个功能 两个线程通过for循环输出0 1 2 3 4 5 6 7 8 9 用c++并发执行来实现。

我们希望程序的输出为:

erlang 复制代码
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
请按任意键继续. . .

那么代码可能会是这样的:

C++ 复制代码
#include<thread>
#include<iostream>
using namespace std;
void enter_region() {
	for (int s = 0; s < 10; s++) cout << s << " "; //输出函数
	cout << endl;	//打印回车
}
int main()
{//并发执行f1与f2线程
	for (int i = 0; i < 3; i++)
	{
		thread f1(enter_region);
		thread f2(enter_region);
		f1.join();//等待f1线程结束
		f2.join();//等待f2线程结束
	}
	system("pause");
}

但此时程序的输出:

erlang 复制代码
00 1 12 2 3 34 54 56 67 78 89 90 01 2 3 4 5 61 72 83 94  
5 6 7 8 9  
0 01 12 23 34 4 55 66 77 88 99  
请按任意键继续. . .

这不是我们想要的程序输出

所以我们需要通过一种方法来限制不让两个线程随意的打印输出到屏幕

下面介绍一种算法来限制两个线程:

C++ 复制代码
//c++实现
#include<thread>
#include<iostream>
using namespace std;
atomic<int> tun;				//线程安全标志位,模拟原子操作
bool i[2] = { false };//初始化
void leave_region(int process)			//退出屏幕打印函数
{
	i[process] = false;
}
void enter_region(int process)				//输出函数
{
	i[process] = true;		//当前进程准备进行屏幕打印
	tun = process;             //将tun设置为要进入屏幕打印的进程
	int other = 1 - process;   //获取另外一个线程
	while (tun == process && i[other] == true);	//循环等待屏幕打印(临界区)空闲
	for (int s = 0; s < 10; s++) cout << s << " "; //输出函数
	cout << endl;	//打印空格
	leave_region(process);  //退出打印屏幕
}
int main()
{//并发执行两个线程
	for (int i = 0; i < 3; i++)l
	{
		thread f1(enter_region,1);
		thread f2(enter_region,0);
		f1.join();//等待f1线程结束
		f2.join();//等待f2线程结束
	}
	system("pause");
}
java 复制代码
//java的实现
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicInteger;

public class fun {
    public static boolean completion[]={false,false}; //初始化
    //标志位.模拟原子操作
    public static AtomicInteger falg=new AtomicInteger();
    public static void main(String[] args) throws InterruptedException {
        for (int j=0;j<10;j++){
            Thread thread1=new Thread(()->enter_region(1));
            Thread thread2=new Thread(()->enter_region(0));
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
        }
    }

    public static void enter_region(int process){ //输出函数 enter_region
        int other=1-process;
        completion[process]=true; ////当前进程准备进行屏幕打印
        falg.set(process);
        while (falg.get()==process&& completion[other]==true);//循环等待进入屏幕打印
        for (int i = 0; i < 10; i++) System.out.print(i+" ");//输出函数
        System.out.println();
        leave_region(process);//退出屏幕打印
    }

    public static void leave_region(int process){
        completion[process]=false;
    }

}

此时打印的数字为:

erlang 复制代码
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9  
请按任意键继续. . .

下面来讲解一下这个算法。现在线程1正在打印屏幕,如果他不调用退出函数,那么线程2里面的i0==true永远为真,所以线程2就会一直在循环(这种情况叫做忙等待),当调用了退出函数,那么线程2也就进行了打印。这种算法就是著名的 Peterson算法 。这么看代码可能不是很好懂,如果感兴趣,你可以尝试用vs实现以下这个代码。 后期增加了java的实现方法,可以自己复制跑一次,在这里打印的屏幕函数段称为临界区 或临界区段。

可能会有人觉得用互斥锁,或者警告变量的方法都可以实现。但是有一个问题需要我们考虑。

先拿锁变量(用一个布尔变量来代表临界区的锁)来说,如果在线程1在查看锁是打开的状态的时候,进程2也发现他是打开的状态,这样就会发生两个进程同时在打印的状态。

再来说说警告变量(用一个值来表示下一个要进入临界区的线程),如果一个线程1做完打印的程序,他就会把变量变成0,但此时线程0并不想进入打印屏幕,而线程1很快又要进入临界区,此时就会发生两个进程都在临界区外的状况。说明这不是 一个很好的方法。

1981年,G.L.peterson发现一种特别好的互斥方法,此方法就是 Peterson算法的。 Peterson 算法的源码为:

C 复制代码
#define FLASE 0
#define TRUE  1
#define N 2                           /*进程的数量*/
int turn;                             /*标志位*/
int interested[N]                     /*所有值初始化为0(FALSE)*/
void enter_region(int process)        /*进程是0或1*/
{
    int other;                       /*定义另一个进程号*/
    other=1-process;                 /*另一个进程*/
    interested[process]=TRUE;        /*表示当前进程需要进入临界区*/
    turn=process                     /*将turn设置为当前进程*/
    while(turn==process&&interested[other]==TRUE);
     /*other进程想要进入临界区,把turn变为other,因为process进程为true便陷入循环(忙等待)但只有当porcess进程调用leave_region退出临界区,此时other进程的enter_region函数while循环interested[other]==TRUE成立才会使other进程退出循环进入临界区*/
}
void leave_region(int process)       /*进程推出临界区调用函数*/
{
    interested(process)=FALSE;       /*进程process退出临界区*/
}

参考书籍:现代操作系统(原书第四版)

相关推荐
小小小小钰儿1 天前
2.3-云端API集成
人工智能·计算机·网络安全·操作系统·编程
做系统的大强1 天前
脚本说 PASS、OS 读全零 —— 到底是谁在撒谎?
后端·操作系统
小小小小钰儿2 天前
2.2-模型微调
人工智能·深度学习·机器学习·计算机·网络安全·操作系统·编程
小小小小钰儿2 天前
2.1-Windows本地部署大模型
人工智能·windows·计算机·网络安全·操作系统·编程
流浪0013 天前
Linux系统篇39——线程(四) pthread库的由来和线程的创建与等待
linux·操作系统·线程·pthread库
流浪0013 天前
Linux系统篇42——线程(七) pthread库管理线程的工作流,从创建到回收
linux·操作系统·线程·线程创建到销毁
云计算练习生4 天前
什么是 Linux?从 Unix 到开源生态的完整脉络
linux·开源·操作系统·unix
云计算练习生5 天前
什么是上下文切换?进程切换时 CPU 到底在保存什么
linux·服务器·操作系统·进程切换
CS创新实验室6 天前
为什么三台机器就能选出一个“带头大哥“?——Raft 一致性算法,一次讲透
分布式·操作系统
sweet丶6 天前
iOS 为什么没有内存交换分区(swap)
操作系统