对于实验环境,利用CLion并通过cmake工具链接到pthread动态库1,使得最终能从Linux的vi编辑器中解脱(成功在win环境下运行),致谢傻狗老公配的cmake工具@zorchp
一些定义:
临界区: 进程互斥就是为了让有共享变量的代码段安全,所以将对共享变量操作的部分称为临界区。
进程锁的尝试1:
p0:do{
while(turn ==1);
//临界区代码
turn=1;
//其他代码
}while(1);
p1:do{
while(turn ==0);
//临界区代码
turn=0;
//其他代码
}while(1);
这样做的好处是:
1.实现了进程的有限等待:等待时间有限
2.实现了进程的互斥性,对于临界区代码,两个进程一定不会一起执行,因为一进入程序判断的就是turn变量,turn标志了该是轮到哪个进程执行就轮到谁执行
缺点与不足:
没有实现进程的进展性原则:
该原则要求:
(1)临界区为空闲的时候,一个要求进入临界区的进程应该立即进入
(2)临界区被某一进程占有时,其他想要进临界区的进程应该处于等待状态
(3)当一个进程离开时,应该允许其他等待进入临界区的进程进入
分析如下:
p0从临界区出来之后,置turn=1,轮到p1执行,p1从临界区出来之后,将turn置0,轮到p0执行,但有一种可能是,p0把turn放在1之后,执行下面的代码,但是此时还没执行完下面的代码,但是轮到p0占有临界区,此时,违反了(1),临界区处于空闲,p1无法进入临界区
演示实验:
#include
#include
#include
#include
int turn;
pthread_t pid1;
pthread_t pid2;
_Noreturn void* fun1(void *p) {
//pthread_join(pid1,NULL);
do {
while (turn == 2);
printf("0\n");
printf("i am the process 1\n");
printf("1\n");
turn = 2;
sleep(3);
} while (1);
}
_Noreturn void* fun2(void *p)
{
//pthread_join(pid1,NULL);
do{
while(turn ==1);
printf("2\n");
printf("i am the process 2\n");
printf("3\n");
turn=1;
sleep(100);
}while(1);
}
int main(void)
{
turn=1;
int a=pthread_create(&pid1,NULL,fun1,NULL);
int b=pthread_create(&pid2,NULL,fun2,NULL);
pthread_join(pid1,NULL);
pthread_join(pid2,NULL);
//这里是为了阻塞,可能主线进程直接没了,但是还没轮到pid1,pid2执行
printf("4\n");
if(a==0&&b==0)
printf("success\n");
printf("bye\n");
结果:
0
i am the process 1
1
2
i am the process 2
3
0
i am the process 1
1
在执行到这里之后就一直不出结果了,的确出现了上述现象
为了对这种现象进行改进,我们只需要让需要进入的置为1,不进入的置为0即可。
进程锁的尝试2:
bool status[2]={0,0};
p0:do{
while(status[1]==1);
//pos1
status[0]=1;
//临界区代码
status[0]=0;
//其他代码
}while(1);
p1:do{
while(status[0]==1);
status[1]=1;
//临界区代码
status[1]=0;
//其他代码
}while(1);
但是这个又有可能发生破坏互斥性,就是两个进程同时运行临界区,多处理机的话初始就同时运行了;
单处理机的话,刚进入一个while,在pos1跳转,肺泡了,
只要不在pos1跳就没事
尝试3:综合以上结果修改算法:(这个算法不是Dekker算法)
bool status[2]={0,0};
int turn =1
p0:do{
while(status[1]==1);
//pos1
if(turn==0)
{ status[0]=1;
//临界区代码
turn=1;
status[0]=0;
//其他代码}
}while(1);
p1:do{
while(status[0]==1);
if(turn==1)
{ status[1]=1;
//临界区代码
turn=0;
status[1]=0;
//其他代码}
}while(1);
事实证明:
经过联合修改之后的程序是没有问题的,不仅实现了互斥效果,而且很好的维持了进展性。使得进程可以交替进行。
实验代码:
#include
#include
#include
#include
int turn;
pthread_t pid1;
pthread_t pid2;
int count=10000;
int a[2];
_Noreturn void* fun1(void *p) {
//pthread_join(pid1,NULL);
do {
while (a[0]==1);
if(turn == 1)
{
a[1]=1;
printf("0\n");
count--;
printf("i am the process 1 :%d\n",count);
printf("1\n");
a[1]=0;
turn = 2;
sleep(3);}
} while (1);
}
_Noreturn void* fun2(void *p)
{
//pthread_join(pid1,NULL);
do{
while(a[1] ==1);
if(turn ==2)
{
a[0]=1;
printf("2\n");
count--;
printf("i am the process 2 :%d\n",count);
printf("3\n");
turn=1;
a[0]=0;
sleep(3);}
}while(1);
}
int main(void)
{
turn=1;
a[0]=0;
a[1]=0;
int a=pthread_create(&pid1,NULL,fun1,NULL);
int b=pthread_create(&pid2,NULL,fun2,NULL);
pthread_join(pid1,NULL);
pthread_join(pid2,NULL);
printf("4\n");
if(a==0&&b==0)
printf("success\n");
printf("bye\n");
}
输出结果
0
i am the process 1 :9999
1
2
i am the process 2 :9998
3
0
i am the process 1 :9997
1
2
i am the process 2 :9996
3
0
i am the process 1 :9995
1
2
i am the process 2 :9994
3
0
i am the process 1 :9993
1
2
i am the process 2 :9992
3
0
i am the process 1 :9991
1
2
i am the process 2 :9990
3
0
i am the process 1 :9989
1
2
i am the process 2 :9988
3
0
i am the process 1 :9987
1
2
i am the process 2 :9986
3
0
i am the process 1 :9985
1
2
i am the process 2 :9984
3
0
i am the process 1 :9983
1
2
i am the process 2 :9982
3
0
i am the process 1 :9981
1
2
i am the process 2 :9980
3
0
i am the process 1 :9979
1
2
i am the process 2 :9978
3
0
i am the process 1 :9977
1
2
i am the process 2 :9976
3
0
i am the process 1 :9975
1
2
i am the process 2 :9974
3
0
i am the process 1 :9973
1
3算法的证明与分析:
对于互斥性,turn变量很好的控制了这一点
对于可进展性:只要0进程从临界区中出来status[0]就变成0,如果没变的话,1进程运行不了,跳回来进程0的时候,status[0]一定变成0,这一句费不了多长时间,然后进程1立刻可以执行,如果进程1一直在其他代码那里运行,其status为0进程0也可以执行,也就是说利用松弛状态数组,和turn变量就可以保持互斥he可进展
对于等待有限性:
如果两个status都是1,才会出现死锁,但是,这个时候两个东西应该在临界区而不是循环等待,所以不会出现死锁。
https://zhuanlan.zhihu.com/p/128519905 ↩︎