北交大操作系统作业进程同步实验.docx
《北交大操作系统作业进程同步实验.docx》由会员分享,可在线阅读,更多相关《北交大操作系统作业进程同步实验.docx(12页珍藏版)》请在冰豆网上搜索。
北交大操作系统作业进程同步实验
进程同步实验2
实验目的2
实验过程2
1.制造混乱2
实验结果:
4
结果分析:
4
2.mutex方案5
实验结果:
6
结果分析:
7
3.软件方案7
实验结果:
10
结果分析:
11
进程同步实验
实验目的
讨论临界区问题及其解决方案。
实验首先创建两个共享数据资源的并发线程。
在没有同步控制机制的情况下,我们将看到某些异常现象。
针对观察到的现象,本实验采用两套解决方案:
•利用Windows的mutex机制
•采用软件方案
然后比较这两种方案的性能优劣。
实验过程
1.制造混乱
Windows操作系统支持抢先式调度,这意味着一线程运行一段时间后,操作系统会暂停其运行并启动另一线程。
也就是说,进程内的所有线程会以不可预知的步调并发执行。
为了制造混乱,我们首先创建两个线程t1和t2。
父线程(主线程)定义两个全局变量,比如accnt1和accnt2。
每个变量表示一个银行账户,其值表示该账户的存款余额,初始值为0。
线程模拟在两个账户之间进行转账的交易。
也即,每个线程首先读取两个账户的余额,然后产生一个随机数r,在其中一个账户上减去该数,在另一个账户上加上该数。
线程操作的代码如下:
#include
#include
#include
intaccnt1=0;
intaccnt2=0;
doublebegin=0,end=0,time=0;
inti=1;
DWORDWINAPIrun(LPVOIDp){
intcounter=0;
inttmp1,tmp2,r;
begin=GetTickCount();
do{
tmp1=accnt1;
tmp2=accnt2;
r=rand();
accnt1=tmp1+r;
accnt2=tmp2-r;
counter++;
}while(accnt1+accnt2==0&&counter<);
end=GetTickCount();
time=end-begin;
printf("counter=%d\n",counter);
printf("进程%d用时%lf\n",i,time);
i++;
counter=0;
return0;
}
intmain(intargc,char*argv[])
{
CreateThread(NULL,0,run,NULL,0,NULL);
CreateThread(NULL,0,run,NULL,0,NULL);
system("PAUSE");
return0;
}
实验结果:
结果分析:
两个线程执行相同的代码。
由于执行过程相互交叉,某线程就有可能访问到新的accnt1值和老的accnt2值,从而导致账户余额数据发生混乱。
线程一旦检测到混乱的发生,便终止循环并打印交易的次数(counter)。
从实验结果可看出,每次进程二可全部完成,但是用时不一定,而进程一不一定能全部完成。
2.mutex方案
利用mutex对象,可以方便地实现临界区保护。
进入临界区时(在第一个读操作之前),锁住mutex对象;离开临界区时(在第二个写操作之后),打开mutex对象。
线程的阻塞与唤醒由系统管理,程序员无需干预。
以下给出的是在Windows操作系统下有关mutex对象操作的代码。
#include
#include
#include
intaccnt1=0;
intaccnt2=0;
doublebegin=0,end=0,time=0;
HANDLEhMutex;
inti=1;
DWORDWINAPIrun(LPVOIDp)
{
intcounter=0;
inttmp1,tmp2,r;
WaitForSingleObject(hMutex,INFINITE);
begin=GetTickCount();
do
{
tmp1=accnt1;
tmp2=accnt2;
r=rand();
accnt1=tmp1+r;
accnt2=tmp2-r;
counter++;
}
while(accnt1+accnt2==0&&counter<);
end=GetTickCount();
time=end-begin;
printf("进程%d用时%lf\n",i,time);
printf("%d\n",counter);
i++;
ReleaseMutex(hMutex);
counter=0;
}
intmain(intargc,char*argv[])
{
hMutex=CreateMutex(NULL,FALSE,NULL);
CreateThread(NULL,0,run,NULL,0,NULL);
CreateThread(NULL,0,run,NULL,0,NULL);
system("PAUSE");
return0;
}
实验结果:
结果分析:
进程一平均用时:
28.4毫秒,进程二平均用时:
28毫秒,进程一和进程二都完成的平均用时是56.4毫秒。
从运行结果可以看出,利用mutex方案可以较为有效地解决临界资源访问问题。
进程一和进程二在次测试中都能够准确完成。
且进程一和进程二用时较为接近。
3.软件方案
假设操作系统没有提供同步原语。
这时,我们只能通过编程语言对变量的操作实现临界区保护。
设计方案如下:
#include
#include
#include
intaccnt1=0,accnt2=0;
DWORDWINAPIrun_1(LPVOIDp)
{
intcounter=0;
inttmp1=0,tmp2=0,r;
doublebegin=0,end=0,time=0;
begin=GetTickCount();
do
{
tmp1=accnt1;
tmp2=accnt2;
r=rand();
accnt1=tmp1+r;
accnt2=tmp2-r;
counter++;
}
while(accnt1+accnt2==0&&counter<);
printf("进程一counter=%d\n",counter);
end=GetTickCount();
time=end-begin;
printf("进程一用时:
%lf\n",time);
counter=0;
return0;
}
DWORDWINAPIrun_2(LPVOIDp)
{
intcounter=0;
inttmp1=0,tmp2=0,r;
doublebegin=0,end=0,time=0;
begin=GetTickCount();
do
{
tmp1=accnt1;
tmp2=accnt2;
r=rand();
accnt1=tmp1+r;
accnt2=tmp2-r;
counter++;
}
while(accnt1+accnt2==0&&counter<);
printf("进程二counter=%d\n",counter);
end=GetTickCount();
time=end-begin;
printf("进程二用时:
%lf\n",time);
counter=0;
return0;
}
intmain(intargc,char*argv[])
{
intc1=0,c2=0;
intwill_wait;
while
(1)
{
c1=1;
will_wait=1;
while(c2&&(will_wait==1));
CreateThread(NULL,0,run_1,NULL,0,NULL);
//break;
c1=0;
break;
}
while
(1)
{
c2=1;
will_wait=2;
while(c1&&(will_wait==2));
CreateThread(NULL,0,run_2,NULL,0,NULL);
//break;
c2=0;
break;
}
system("PAUSE");
return0;
}
实验结果:
结果分析:
从进程一和进程二中选取都完成了次计算的进程,选取五次计算平均结果。
得
进程一平均用时:
31毫秒,进程二平均用时:
25.2毫秒,进程一和进程二完成的平均用时是56.2毫秒.mutex方案与软件方案相比,用时较为接近,进程一和进程二的执行前后顺序不一定。
但是在软件方案中,进程一和进程二在测试中不一定能够执行完次,此方案不能对进程有效加解锁。
疑问:
从代码分析,c1和c2解决了资源的互斥访问,will_wait解决了“饥饿”现象,但是为什么执行结果和分析的不一样?