第2次实验14281147王飞.docx
- 文档编号:10647534
- 上传时间:2023-02-22
- 格式:DOCX
- 页数:22
- 大小:86.89KB
第2次实验14281147王飞.docx
《第2次实验14281147王飞.docx》由会员分享,可在线阅读,更多相关《第2次实验14281147王飞.docx(22页珍藏版)》请在冰豆网上搜索。
第2次实验14281147王飞
实验二进程同步
1.实验目的
●理解进程并发执行的特征
●掌握进程同步的基本方法
●基于信号量编程实现进程同步
●基于互斥锁实现线程同步问题。
2.实验内容
1)创建4个进程P1-P4。
要求进程P1在P2,P3之前,P4在其他进程之后执行。
P2、P3两个进程互斥。
用信号量机制实现同步与互斥,并用实验验证。
步骤如下
a.分析:
要实现P1在P2,P3之前,P4在P2,P3之后,P2,P3互斥,则需要引入三个信号量sem_id,sem_id1,sem_id2,三个信号量的初值均设置为0。
其控制关系如下
b.利用fork()函数创建四个进程P1,P2,P3,P4,然后根据
(1)中的控制关系,定义三个信号量,并进行绑定,在每个进程里面编写打印语句以便观察。
编写得到的程序如附fourPro.c所示。
为使观察得到的结果更具有说服力,创建的四个进程的的父子关系为
c.控制台输入gccfourPro.c,点击回车,再输入./a.out,得到如下运行结果:
可以看到,四个进程的执行顺序为
P1->P2->P3->P4
满足进程P1在P2,P3之前,P4在P2,P3之后,P2与P3互斥
2)生产者消费者同步。
生产者进程将生产的数据写入文件中。
消费者读取文件中的数据。
注意,传送的每个数据都是一个字符,文件中最多可写入10个字符。
编程实现并用实验验证结果。
程序如附proandcons.c所示,程序中使用了共享存储区的方式来实现两个进程间的通信。
共享存储区内可存放十个字符,另外,用i和tail来标记下一个可以消费的位置和可以存放新字符的位置。
每次,生产者将生产的字符放入存储区第tail个空余的位置,消费者消费第i个位置。
执行部分结果如下
基本实现了生产者消费者的要求。
3)假设有两个银行账户,初始金额值分别为intnAccount1=0,nAccount2=0;下面是在这两个银行账户间转账一个随机金额的函数transfer。
试创建两个转账线程使其并发执行。
(1)观察无同步控制时,两个线程并发执行的可能次序;观察账户金额的错误现象(正确的结果应该是两个账户金额之和始终为0)。
分析在何种执行次序时会导致错误
(2)分别利用互斥锁、Peterson方法实现两个线程互斥,并比较两者时间效率的差异。
(1)A.不使用同步控制的情况下,代码如附bank.c所示,为了使得更好地观察运行的结果,对transfer函数做如下改造
voidtransfer(){
intnTemp1,nTemp2,nRandom;
do
{
nTemp1=nAccount1;
sleep
(1);//添加延时
nTemp2=nAccount2;
nRandom=rand();
nAccount1=nTemp1+nRandom;
nAccount2=nTemp2-nRandom;
printf("nAccount1=%d,nAccount2=%d\n",nAccount1,nAccount2);
printf("账户金额之和=%d\n",nAccount1+nAccount2);
}while(nAccount1+nAccount2==0);
//和不为0的时候跳出循环
printf("NOtequal!
!
!
\n");
}
B.在控制台运行该程序,得到的结果如下:
可以发现,出现了不等于0的情况,说明在没有进程同步控制下,会发生错误。
出现错误的次序:
其中一个线程执行完nTemp1=nAccount1;然后另一个线程开始执行nAccount1=nTemp1+nRandom,该线程继续执行nAccount2=nTemp2+nRandom,执行完成之后,前面的线程开始执行nTemp2=nAccount2,此时,nTemp1存储的值是nAccount1为修改前的值,而nAccount2的值已经被另一个线程修改过,造成后面加减完同一个随机数后,两者之和不为0.
(2).A.使用互斥锁,程序如附inOrder.c所示,运行结果如下
可以看到,输出的值始终保持和为0,没有出现和不为0的情况。
这是因为,在每次只允许一个线程对nAccount1和nAccount2进行修改,只有当一个线程完成了对这两个量的修改之后,另一个线程才有机会执行修改操作。
B.Peterson法,程序如附peterson.c所示,执行效果如下图所示
Peterson方法的思想就是通过三个标志位来控制两个线程对临界区的访问。
算法使用两个控制变量flag与turn.其中flag[n]的值为真,表示ID号为n的进程希望进入该临界区.标量turn保存有权访问共享资源的进程的ID号。
两个线程并发时,turn会使得两个线程必然不会同时满足while循环的条件,跳过while循环之后,线程会将对方的标志位置位0,这时while条件不再满足,即可开始执行自己的程序。
C.互斥锁和peterson方法时间效率的差异。
删除之前为方便观察添加的延时函数,可以两个程序中的每个线程都对账户余额操作10000次(即把transfer函数执行1000次),利用计时器测量程序执行时间来对比效率差异。
(方法:
C语言time.h提供结构体time_t,该变量使用方式为:
time_ta=time(NULL),中便会存储当前时间距离计算机计时起始点的秒数,程序设置两个这样的变量,在分别放在程序开头和结尾,即可实现对程序运行时间的粗略计时)
a.互斥锁方法运行结果:
2s(执行1000次显示时间为0,所以改为执行10000次)
b.peterson方法运行结果:
81s(执行1000次)
从运行结果可以看出,互斥锁的时间效率要高于peterson方法。
原因可能相对于互斥锁方法,peterson方法需要花费很多时间在flag和turn值的比较上。
附:
各部分代码
fourProc.c
/*sem_fork.c*/
#include
#include
#include
#include
#include
#include
/*定义联合体*/
unionsemun
{
intval;
structsemid_ds*buf;
unsignedshort*array;
};
/*函数声明*/
intinit_sem(intsem_id,intinit_value);/*信号量初始化(赋值)函数*/
intdel_sem(intsem_id);/*从系统中删除信号量的函数*/
intsem_p(intsem_id);/*P操作函数*/
intsem_v(intsem_id);/*V操作函数*/
intmain(void)
{
pid_tresult,result1,result2;
intsem_id,sem_id1,sem_id2;
/*创建一个信号量*/
sem_id=semget(ftok(".",'a'),1,0666|IPC_CREAT);
init_sem(sem_id,0);
/*创建一个信号量*/
sem_id1=semget(ftok(".",'b'),1,0666|IPC_CREAT);
init_sem(sem_id1,0);
/*创建一个信号量*/
sem_id2=semget(ftok(".",'c'),1,0666|IPC_CREAT);
init_sem(sem_id2,0);
/*调用fork()函数*/
while((result=fork())==-1);
if(result==0)/*返回值为0代表子进程*/
{
while((result2=fork())==-1);
if(result2==0)
{
printf("I'mP1withPID=%d,parent=%d\n",getpid(),getppid());
sem_v(sem_id);
exit(0);
}
else
{
sem_p(sem_id);
printf("I'mP2withPID=%d,parent=%d\n",getpid(),getppid());
sem_v(sem_id1);
sem_v(sem_id);
wait(0);
}
}
else/*返回值大于0代表父进程*/
{
while((result1=fork())==-1);
if(result1==0)
{
sem_p(sem_id);
printf("I'mP3withPID=%d,parent=%d\n",getpid(),getppid());
sem_v(sem_id2);
sem_v(sem_id);
exit(0);
}
else
{
sem_p(sem_id1);
sem_p(sem_id2);
printf("I'mP4withPID=%d,parent=%d\n",getpid(),getppid());
sem_v(sem_id2);
sem_v(sem_id1);
del_sem(sem_id);
del_sem(sem_id1);
del_sem(sem_id2);
wait(0);
}
//wait(0);
}
exit(0);
}/*main()end*/
/*信号量初始化(赋值)函数*/
intinit_sem(intsem_id,intinit_value)
{
unionsemunsem_union;
sem_union.val=init_value;/*init_value为初始值*/
if(semctl(sem_id,0,SETVAL,sem_union)==-1)
{
perror("Initializesemaphore");
return-1;
}
return0;
}
/*从系统中删除信号量的函数*/
intdel_sem(intsem_id)
{
unionsemunsem_union;
if(semctl(sem_id,0,IPC_RMID,sem_union)==-1)
{
perror("Deletesemaphore");
return-1;
}
}
/*P操作函数*/
intsem_p(intsem_id)
{
structsembufsem_b;
sem_b.sem_num=0;/*信号量编号,这里单个信号量的编号应该为0*/
sem_b.sem_op=-1;/*信号量操作,取值为-1表示P操作*/
sem_b.sem_flg=SEM_UNDO;/*在进程没释放信号量而退出时,系统自动释放该进程中
未释放的信号量*/
if(semop(sem_id,&sem_b,1)==-1)/*进行P操作*/
{
perror("Poperation");
return-1;
}
return0;
}
/*V操作函数*/
intsem_v(intsem_id)
{
structsembufsem_b;
sem_b.sem_num=0;/*信号量编号,这里单个信号量的编号应该为0*/
sem_b.sem_op=1;/*信号量操作,取值为+1表示V操作*/
sem_b.sem_flg=SEM_UNDO;/*在进程没释放信号量而退出时,系统自动释放该进程中
未释放的信号量*/
if(semop(sem_id,&sem_b,1)==-1)/*进行V操作*/
{
perror("Voperation");
return-1;
}
return0;
}
proandcons.c
#include"stdio.h"
#include"sys/types.h"
#include"unistd.h"
#include"stdlib.h"
#include
#include
#include
#defineHASPRO-10
unionsemun
{
intval;
structsemid_ds*buf;
unsignedshort*array;
};
structshared_area
{
inti;
chartext[10];
inttail;
};
voidbuildShare();
voidproduce();
voidconsume();
intsem_v(intsem_id);
intsem_p(intsem_id);
intinit_sem(intsem_id,intinit_value);
structshared_area*sha;
intsem_id;
intempty;
intfull;
intmain(void)
{
intresult,result1;
sem_id=semget(ftok(".",'a'),1,0666|IPC_CREAT);
init_sem(sem_id,1);
empty=semget(ftok(".",'b'),1,0666|IPC_CREAT);
init_sem(empty,10);
full=semget(ftok(".",'c'),1,0666|IPC_CREAT);
init_sem(full,0);
while((result=fork())==-1);
if(result==0){
while((result1=fork())==-1);
if(result1==0){
buildShare();
while
(1){
consume();
}
exit(0);
}
else{
buildShare();
while
(1){
produce();
}
wait(0);
}
exit(0);
}
else{
wait(0);
}
return0;
}
voidconsume()
{
sem_p(full);
sem_p(sem_id);
printf("readout%d\n",sha->text[sha->i]);
sha->i=(sha->i+1)%10;
sem_v(sem_id);
sem_v(empty);
}
voidproduce()
{
sem_p(empty);
sem_p(sem_id);
sha->text[sha->tail]=sha->tail;
printf("writein%d\n",sha->tail);
sha->tail=(sha->tail+1)%10;
sem_v(sem_id);
sem_v(full);
}
voidbuildShare()
{
intshmid;
void*shm=NULL;
shmid=shmget((key_t)1234,sizeof(structshared_area),0666|IPC_CREAT);
while(shmid==-1)
{
shmid=shmget((key_t)1234,sizeof(structshared_area),0666|IPC_CREAT);
}
shm=shmat(shmid,0,0);
if(shm==(void*)-1)
{
fprintf(stderr,"shmatfailed\n");
exit(EXIT_FAILURE);
}
sha=(structshared_area*)shm;
sha->i=0;
sha->tail=0;
}
/*信号量初始化(赋值)函数*/
intinit_sem(intsem_i,intinit_value)
{
unionsemunsem_union;
sem_union.val=init_value;/*init_value为初始值*/
if(semctl(sem_i,0,SETVAL,sem_union)==-1)
{
perror("Initializesemaphore");
return-1;
}
return0;
}
/*P操作函数*/
intsem_p(intsem_i)
{
structsembufsem_b;
sem_b.sem_num=0;/*信号量编号,这里单个信号量的编号应该为0*/
sem_b.sem_op=-1;/*信号量操作,取值为-1表示P操作*/
if(semop(sem_i,&sem_b,1)==-1)/*进行P操作*/
{
perror("Poperation");
return-1;
}
return0;
}
/*V操作函数*/
intsem_v(intsem_i)
{
structsembufsem_b;
sem_b.sem_num=0;/*信号量编号,这里单个信号量的编号应该为0*/
sem_b.sem_op=1;/*信号量操作,取值为+1表示V操作*/
if(semop(sem_i,&sem_b,1)==-1)/*进行V操作*/
{
perror("Voperation");
return-1;
}
return0;
}
bank.c
#include
#include
pthread_mutex_tmutex;
intnAccount1=0;
intnAccount2=0;
voidtransfer();
intmain(intargc,char**argv)
{
pthread_tid1;
pthread_tid2;
pthread_mutex_init(&mutex,NULL);
pthread_create(&id1,NULL,transfer,NULL);
pthread_create(&id2,NULL,transfer,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&mutex);
return1;
}
voidtransfer(){
intnTemp1,nTemp2,nRandom;
do
{
nTemp1=nAccount1;
sleep
(1);
nTemp2=nAccount2;
nRandom=rand();
nAccount1=nTemp1+nRandom;
nAccount2=nTemp2-nRandom;
printf("nAccount1=%d,nAccount2=%d\n",nAccount1,nAccount2);
printf("账户金额之和=%d\n",nAccount1+nAccount2);
}while(nAccount1+nAccount2==0);
printf("NOtequal!
!
!
\n");
}
inOrder.c
#include
#include
pthread_mutex_tmutex;
intnAccount1=0;
intnAccount2=0;
voidtransfer();
intmain(intargc,char**argv)
{
pthread_tid1;
pthread_tid2;
pthread_mutex_init(&mutex,NULL);
pthread_create(&id1,NULL,transfer,NULL);
pthread_create(&id2,NULL,transfer,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&mutex);
return1;
}
voidtransfer(){
intnTemp1,nTemp2,nRandom;
do
{
pthread_mutex_lock(&mutex);
nTemp1=nAccount1;
sleep
(1);
nTemp2=nAccount2;
nRandom=rand();
nAccount1=nTemp1+nRandom;
nAccount2=nTemp2-nRandom;
printf("nAccount1=%d,nAccount2=%d\n",nAccount1,nAccount2);
printf("账户金额之和=%d\n",nAccount1+nAccount2);
pthread_mutex_unlock(&mutex);
}while(nAccount1+nAccount2==0);
}
Peterson.c
#include
#include
unsignedintflag[2]={0};//想进入临界区的意愿
unsignedintturn=1;//轮到哪个进程进入临界区
intnAccount1=0;
intnAccount2=0;
voidtransfer();
void*process1()
{
while
(1)
{
flag[0]=1;
turn=0;
while(flag[0]&&(turn==0));
{
transfer();
}
flag[1]=0;
}
returnNULL;
}
void*process2()
{
while
(1)
{
flag[1]=1;
turn=1;
while(flag[1]&&(turn==1
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验 14281147 王飞
![提示](https://static.bdocx.com/images/bang_tan.gif)