linux多线程实验报告.docx
《linux多线程实验报告.docx》由会员分享,可在线阅读,更多相关《linux多线程实验报告.docx(27页珍藏版)》请在冰豆网上搜索。
![linux多线程实验报告.docx](https://file1.bdocx.com/fileroot1/2023-2/23/74fa157b-95c0-4ea7-b5af-ac8533a47a68/74fa157b-95c0-4ea7-b5af-ac8533a47a681.gif)
linux多线程实验报告
实验八Linux多线程实验
报告撰写人
专业班级
学号
完成时间
。
。
。
。
。
。
。
。
。
。
。
一、实验目的
1、了解什么是多线程,熟悉LINUX的多线程机制;
2、掌握利用信号处理Linux多线程的同步问题;
3、掌握利用信号量处理Linux多线程的互斥问题;
4、运用Linux多线程的同步机制和互斥机制实现生产者消费者的编程。
二、实验内容
1.“生产者-消费者”问题如下:
有一个有限缓冲区和两个线程:
生产者和消费者。
他们分别不停地把产品放入缓冲区、从缓冲区中拿走产品。
一个生产者在缓冲区满的时候必须等待,一个消费者在缓冲区空的时候也必须等待。
另外,因为缓冲区是临界资源,所以生产者和消费者之间必须互斥执行。
它们之间的关系如图1所示。
现在要求使用共享内存来模拟有限缓冲区,并且使用信号量来解决“生产者-消费者”问题中的同步和互斥问题。
1生产者和消费者问题描述
2.问题描述:
假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:
吃饭,或者思考。
吃东西的时候,他们就停止思考,思考的时候也停止吃东西。
餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。
因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。
他们只能使用自己左右手边的那两只餐叉。
请用Linux线程编程解决。
2哲学家进餐问题示意图
三、实验过程与结果
操作过程
错误
解决方法
实验1
步骤一:
编写producer_customer.c的文件
在不同的编译器中for循环的用法不太一样,在这里最好不要使用for(inti=0;;)
最好在外声明inti在for循环里直接写i=0
步骤二:
编译代码并且运行代码
步骤三:
运行完毕后,显示以下代码:
生产者消费者先后生产数据以及取出数据,此程序中设置了两个消费者两个生产者,交替进行生产数据消费数据。
实验2
步骤一:
编写philosopher.c的文件
步骤二:
编译代码并且运行代码
步骤三:
实验代码
实验一:
#include
#include
#include
#include
#include
#include
#include
#include
#defineMAX_BUFFER_SIZE10
#defineSHM_MODE0600
#defineSEM_MODE0600
#defineSEM_FULL0
#defineSEM_EMPTY1
#defineMUTEX2
/*
#ifdefined(__GNU_LIBRARY__)&&!
defined(_SEM_SEMUN_UNDEFINED)
//unionsemunisdefinedbyincluding
#else
//accordingtoX/OPENwehavetodefineitourselves
unionsemun{
intval;
structsemid_ds*buf;
unsignedshort*array;
};
#endif
unionsemunsu;//semunion,用于初始化信号量
*/
structmy_buffer
{
inthead;
inttail;
charstr[MAX_BUFFER_SIZE];
intnum;//缓冲区里字母数量
intis_empty;
};
constintN_CONSUMER=2;//消费者数量
constintN_PRODUCER=2;//生产者数量
constintN_BUFFER=10;//缓冲区容量
constintN_WORKTIME=10;//工作次数
intshm_id=-1;
intsem_id=-1;
pid_tchild;
pid_tparent;
//得到10以内的一个随机数
intget_random()
{
intdigit;
srand((unsigned)(getpid()+time(NULL)));
digit=rand()%10;
returndigit;
}
//得到A~Z的一个随机字母
chargetRandChar()
{
charletter;
srand((unsigned)(getpid()+time(NULL)));
letter=(char)((rand()%26)+'A');
returnletter;
}
//sem_id表示信号量集合的id
//sem_num表示要处理的信号量在信号量集合中的索引
//P操作
voidwaitSem(intsem_id,intsem_num)
{
structsembufsb;
sb.sem_num=sem_num;
sb.sem_op=-1;//表示要把信号量减一
sb.sem_flg=SEM_UNDO;//
//第二个参数是sembuf[]类型的,表示数组
//第三个参数表示第二个参数代表的数组的大小
if(semop(sem_id,&sb,1)<0){
perror("waitSemfailed");
exit
(1);
}
}
//V操作
voidsigSem(intsem_id,intsem_num)
{
structsembufsb;
sb.sem_num=sem_num;
sb.sem_op=1;
sb.sem_flg=SEM_UNDO;
//第二个参数是sembuf[]类型的,表示数组
//第三个参数表示第二个参数代表的数组的大小
if(semop(sem_id,&sb,1)<0){
perror("sigSemfailed");
exit
(1);
}
}
//打印进程运行结果
voidprintTime()
{
//打印时间
time_tnow;
structtm*timenow;//实例化tm结构指针
time(&now);
timenow=localtime(&now);
printf("执行时间:
%s",asctime(timenow));
}
intmain(intargc,char**argv)
{
shm_id=shmget(IPC_PRIVATE,MAX_BUFFER_SIZE,SHM_MODE);//申请共享内存
if(shm_id<0)
{
perror("createsharedmemoryfailed");
exit
(1);
}
structmy_buffer*shmptr;
shmptr=shmat(shm_id,0,0);//将申请的共享内存附加到申请通信的进程空间
if(shmptr==(void*)-1)
{
perror("addbuffertousingprocessspacefailed!
\n");
exit
(1);
}
if((sem_id=semget(IPC_PRIVATE,3,SEM_MODE))<0)
{//创建三个信号量,SEM_EMPTY,SEM_FULL和MUTEX
perror("createsemaphorefailed!
\n");
exit
(1);
}
if(semctl(sem_id,SEM_FULL,SETVAL,0)==-1)
{//将索引为0的信号量设置为0-->SEM_FULL
perror("semsetvalueerror!
\n");
exit
(1);
}
if(semctl(sem_id,SEM_EMPTY,SETVAL,10)==-1)
{//将索引为1的信号量设置为10-->SEM_EMPTY
perror("semsetvalueerror!
\n");
exit
(1);
}
if(semctl(sem_id,MUTEX,SETVAL,1)==-1)
{//将索引为3的信号量设置为1-->MUTEX
perror("semsetvalueerror!
\n");
exit
(1);
}
shmptr->head=0;
shmptr->tail=0;
shmptr->is_empty=1;
shmptr->num=0;
inti;
for(i=0;i{
parent=fork();
if(parent<0)
{
perror("theforkfailed");
exit
(1);
}
elseif(parent==0)
{
shmptr=shmat(shm_id,0,0);//将申请的共享内存附加到申请通信的进程空间
if(shmptr==(void*)-1)
{
perror("addbuffertousingprocessspacefailed!
\n");
exit
(1);
}
intcount=0;
intj;
for(j=0;j{
waitSem(sem_id,SEM_EMPTY);
waitSem(sem_id,MUTEX);
sleep(get_random());
printf("-------------------------------------------------------------\n");
printf("我是第%d个生产者进程,PID=%d\n",i+1,getpid());
/*生产产品*/
charc=getRandChar();//随机获取字母
shmptr->str[shmptr->tail]=c;
shmptr->tail=(shmptr->tail+1)%MAX_BUFFER_SIZE;
shmptr->is_empty=0;//写入新产品
shmptr->num++;
/*打印输出结果*/
printTime();//程序运行时间
intp;
printf("缓冲区数据(%d个):
",shmptr->num);//打印缓冲区中的数据
p=(shmptr->tail-1>=shmptr->head)?
(shmptr->tail-1):
(shmptr->tail-1+MAX_BUFFER_SIZE);
for(p;!
(shmptr->is_empty)&&p>=shmptr->head;p--)
{
printf("%c",shmptr->str[p%MAX_BUFFER_SIZE]);
}
printf("\t生产者%d放入'%c'.\n",i+1,c);
printf("-------------------------------------------------------------\n");
fflush(stdout);
sigSem(sem_id,MUTEX);
sigSem(sem_id,SEM_FULL);
}
//将共享段与进程之间解除连接
shmdt(shmptr);
exit(0);
}
}
for(i=0;i{
child=fork();
if(child<0)//调用fork失败
{
perror("theforkfailed");
exit
(1);
}
elseif(child==0)
{
intcount=0;
shmptr=shmat(shm_id,0,0);//将申请的共享内存附加到申请通信的进程空间
if(shmptr==(void*)-1)
{
perror("addbuffertousingprocessspacefailed!
\n");
exit
(1);
}
intj;
for(j=0;j{
waitSem(sem_id,SEM_FULL);
waitSem(sem_id,MUTEX);
sleep(get_random());
printf("-------------------------------------------------------------\n");
printf("我是第%d个消费者进程,PID=%d\n",i+1,getpid());
/*消费数据*/
charlt=shmptr->str[shmptr->head];
shmptr->head=(shmptr->head+1)%MAX_BUFFER_SIZE;
shmptr->is_empty=(shmptr->head==shmptr->tail);//
shmptr->num--;
/*打印输出结果*/
printTime();//程序运行时间
intp;
printf("缓冲区数据(%d个):
",shmptr->num);//打印缓冲区中的数据
p=(shmptr->tail-1>=shmptr->head)?
(shmptr->tail-1):
(shmptr->tail-1+MAX_BUFFER_SIZE);
for(p;!
(shmptr->is_empty)&&p>=shmptr->head;p--)
{
printf("%c",shmptr->str[p%MAX_BUFFER_SIZE]);
}
printf("\t消费者%d取出'%c'.\n",i+1,lt);
printf("-------------------------------------------------------------\n");
fflush(stdout);
sigSem(sem_id,MUTEX);
sigSem(sem_id,SEM_EMPTY);
}
//将共享段与进程之间解除连接
shmdt(shmptr);
exit(0);
}
}
//主进程最后退出
while(wait(0)!
=-1);
//将共享段与进程之间解除连接
shmdt(shmptr);
//对共享内存区执行控制操作
shmctl(shm_id,IPC_RMID,0);//当cmd为IPC_RMID时,删除该共享段
shmctl(sem_id,IPC_RMID,0);
printf("主进程运行结束!
\n");
fflush(stdout);
exit(0);
return0;
}
实验二:
#include
#include
#include
#include
#include
#include
#include
#defineN5//哲学家数量
#defineLEFT(i)(i+N-1)%N//左手边哲学家编号
#defineRIGHT(i)(i+1)%N//右手边哲家编号
#defineHUNGRY0//饥饿
#defineTHINKING1//思考
#defineEATING2//吃饭
#defineU_SECOND1000000//1秒对应的微秒数
pthread_mutex_tmutex;//互斥量
intstate[N];//记录每个哲学家状态
//每个哲学家的思考时间,吃饭时间,思考开始时间,吃饭开始时间
clock_tthinking_time[N],eating_time[N],start_eating_time[N],start_thinking_time[N];
//线程函数
void*thread_function(void*arg);
intmain()
{
pthread_mutex_init(&mutex,NULL);
pthread_ta,b,c,d,e;
//为每一个哲学家开启一个线程,传递哲学家编号
pthread_create(&a,NULL,thread_function,"0");
pthread_create(&b,NULL,thread_function,"1");
pthread_create(&c,NULL,thread_function,"2");
pthread_create(&d,NULL,thread_function,"3");
pthread_create(&e,NULL,thread_function,"4");
//初始化随机数种子
srand((unsignedint)(time(NULL)));
while
(1)
{
;
}
}
void*thread_function(void*arg)
{
char*a=(char*)arg;
intnum=a[0]-'0';//根据传递参数获取哲学家编号
intrand_time;
while
(1)
{
//关键代码加锁
pthread_mutex_lock(&mutex);
//如果该哲学家处于饥饿并且左右两位哲学家都没有在吃饭就拿起叉子吃饭
if(state[num]==HUNGRY&&state[LEFT(num)]!
=EATING&&state[RIGHT(num)]!
=EATING)
{
state[num]=EATING;
start_eating_time[num]=clock();//记录开始吃饭时间
eating_time[num]=(rand()%5+5)*U_SECOND;//随机生成吃饭时间
//输出状态
printf("state:
%d%d%d%d%d\n",state[0],state[1],state[2],state[3],state[4]);
//printf("%diseating\n",num);
}
elseif(state[num]==EATING)
{
//吃饭时间已到,开始思考
if(clock()-start_eating_time[num]>=eating_time[num])//
{
state[num]=THINKING;
//printf("%disthinking\n",num);
printf("state:
%d%d%d%d%d\n",state[0],state[1],state[2],state[3],state[4]);
start_thinking_time[num]=clock();//记录开始思考时间
thinking_time[num]=(rand()%10+10)*U_SECOND;//随机生成思考时间
}
}
elseif(state[num]==THINKING)
{
//思考一定时间后,哲学家饿了,需要吃饭
if(clock()-start_thinking_time[num]>=thinking_time[num])
{
state[num]=HUNGRY;
printf("state:
%d%d%d%d%d\n",state[0],state[1],state[2],state[3],state[4]);
//printf("%dishungry\n",num);
}
}
pthread_mutex_unlock(&mutex);
}
}
四、实验小结
生产者消费者问题(英语:
Producer-consumerproblem),也称有限缓冲问题(英语:
Bounded-bufferproblem),是一个多线程同步问题的经典案例。
该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。
生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。
与此同时,消费者也在缓冲区消耗这些数据。
该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
1)关系分析。
生产者和消费者对缓冲区互斥访问是互斥关系,同时生产者和消费者又是一个相互协作的关系,只有生产者生产之后,消费者才能消费,他们也是同步关系。
2)整理思路。
这里比较简单,只有生产者和消费者两个进程,正好是这两个进程存在着互斥关系和同步关系。
那么需要解决的是互斥和同步PV操作的位置。
3)信号量设置。
信号量mutex作为互斥信号量,它用于控制互斥访问缓冲池,互斥信号量初值为1;信号量full用于记录当前缓冲池中“满”缓冲区数,初值为0。
信号量empty用于记录当前缓冲池中“空”缓冲区数,初值为n。