信号量控制线程同步课程实践报告.docx
《信号量控制线程同步课程实践报告.docx》由会员分享,可在线阅读,更多相关《信号量控制线程同步课程实践报告.docx(16页珍藏版)》请在冰点文库上搜索。
![信号量控制线程同步课程实践报告.docx](https://file1.bingdoc.com/fileroot1/2023-5/25/33dfcc16-f041-422d-9ec3-ec4db331e6d0/33dfcc16-f041-422d-9ec3-ec4db331e6d01.gif)
信号量控制线程同步课程实践报告
课程设计说明书
2021—2021学年第一学期
题目:
信号量控制线程同步
学院:
计算机学院
专业班级:
学号:
学生姓名:
指导教师:
成绩:
时间:
8:
00—10:
00
2021年12月6日
课程设计任务书
2021~2021学年第一学期
学生姓名:
专业班级:
指导教师:
工作部门:
计算机学院
一、课程设计题目
信号量控制线程同步
二、课程设计内容
该任务含两个程序的设计与编写,程序1同步两个线程,其一输出奇数,另一线程输出偶数,且二者交叉数据输出;程序2实现男女共用浴室的情况下的同步与互斥,男女到达浴室的时间数据放在文本文件。
对于给定的输入文件,观察测试输出结果的正确性。
三、进度安排
〔1〕1-2学时,选定题目、分析需求、理解需求;
〔2〕3-4学时,程序设计,定义数据类型、数据处理方式;
〔3〕5-10学时,编写程序、调试、测试;
〔4〕11-12学时,编写设计报告;
〔5〕13-16学时,辩论。
四、根本要求
〔1〕使用偏好的编程语言,源程序要有适当的注释,使程序容易阅读
〔2〕学生可自动增加新功能模块〔视情况可另外加分〕。
〔3〕写出课程设计报告,应不少于3000字〔不含附录〕,同一组学生每人必须提交1份,报告中列明分工。
课程负责人签名:
年月日
课程设计成绩评定表
姓名
成绩评定权重
总分
总成绩
〔五分制〕
平时成绩20
报告成绩50
辩论成绩30
信号量控制线程同步
摘要
信号量控制线程同步算法,主要设置两个线程,用一个或两个信号量分别控制线程,使得线程交替输出,资源临界区信号的控制利用,使用二个信号量,控制二线程其中之一输出一个数后,随后另一个线程也输出一个数,交替输出直至结束。
以及实现:
当有一个女生在浴室里,那么其他女生可以进入,但是男生不行,反之亦然。
关键词:
线程同步信号量临界区
1设计内容
编写信号量控制线程同步的算法,实现以下功能:
(1)两个线程,一者输出1-30的偶数,一者输出1-30的奇数
(2)使用一个信号量,控制二线程分别输出〔即一个线程完成数据输出后,另一个线程方可开始数据输出。
〕
(3)使用二个信号量,控制二线程其中之一输出一个数后,随后另一个线程也输出一个数,交替输出直至结束。
(4)当有一个女生在浴室里,那么其他女生可以进入,但是男生不行,反之亦然。
(5)本系统是控制台应用程序,界面非常直观
2总体设计
:
用用一个信号量,两个线程控制奇偶数的交替输出以及两个线程控制先输出奇数再输出偶数的程序。
男女共浴问题先要设置洗澡时间,男人和女人到达浴室的时间,还要读写文件,并且在文件中显示出最后的结果。
程序要设计得简单,可读性强,运行效率高,占用内存小,有一定的健壮性和良好的可维护性。
2.2设计调用图
图2-1设计调用图1
图2-2设计调用图2
3详细设计
数据结构设计:
这两个程序使用C#编写:
程序1:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Threading;
namespaceConsoleApplication1{};//命名空间
privatestaticvoidThread_1();//控制奇数输出的线程函数
privatestaticvoidThread_2();//控制偶数输出的线程函数
程序2:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.Threading;
usingSystem.IO;
namespaceConsoleApplication2{};//命名空间
publicstaticvoidMale(refint[]data);//
publicstaticvoidFemale(refint[]data);//
privatestaticvoidMaleThread();//
privatestaticvoidFemaleThread()//
流程图:
4调试与测试
调试过程主要问题:
pow(2,k-i)的不一致,VS2021中老是提示函数调用不明确,导致遇到了一些问题,不过最终还是找到的问题的所在,那就是double型和int行的隐式转换问题。
还有对于文件存储问题是在不知道怎么去处理,又加上是在对并不怎么熟悉的伙伴系统之中,只好舍之〔当然,是为了能够正确测试出程序的可运行性和直观性〕。
经过这次的课程设计,还是学会了编程不但需要头脑,还要很细心才行。
4.2测试结果
1内存申请与回收后块的大小变化显示
附录
//MMC.cpp:
Definestheentrypointfortheconsoleapplication.
//
#include"stdafx.h"
/*algo8-2.c伙伴系统。
实现算法8.2的程序*/
#include"c1.h"
#include"c8-2.h"
#defineN100/*占用块个数的最大值*/
Spacer;/*r为生成空间的首地址,全局量*/
unsignedchar*p;
unsignedchargBuffer[256*1024];//长度为0~255的char类型
SpaceAllocBuddy(FreeListavail,intn)
{/*avail[0..m]为可利用空间表,n为申请分配量,假设有不小于n的空闲块,*/
/*那么分配相应的存储块,并返回其首地址;否那么返回NULL。
算法8.2*/
inti,k;
Spacepa,pi,pre,suc;
for(k=0;k<=m&&(avail[k].nodesizeavail[k].first);++k);/*从小到大查找满足分配要求的子表序号k*/
if(k>m)/*分配失败,返回NULL*/
returnNULL;
else/*进行分配*/
{
pa=avail[k].first;/*pa指向可分配子表的第一个结点*/
pre=pa->llink;/*pre和suc分别指向pa所指结点的前驱和后继*/
suc=pa->rlink;
if(pa==suc)/*可分配子表只有1个结点*/
avail[k].first=NULL;/*分配后该子表变成空表*/
else/*从子表中删去pa所指结点(链表的第1个结点)*/
{
pre->rlink=suc;
suc->llink=pre;
avail[k].first=suc;/*该子表的头指针指向pa所指结点的后继*/
}
for(i=1;avail[k-i].nodesize>=n+1;++i)
{/*从大到小将剩余块插入相应子表,约定将低地址(最前面)的块作为分配块*/
pi=pa+(int)pow(2,k-i);/*pi指向再分割的后半块(剩余块)*/
pi->rlink=pi;/*pi是该链表的第1个结点,故左右指针都指向自身*/
pi->llink=pi;
pi->tag=0;/*块标志为空闲*/
pi->kval=k-i;/*块大小*/
avail[k-i].first=pi;/*插入链表*/
}
pa->tag=1;/*最后剩给pa的是分配块,令其块标志为占用*/
pa->kval=k-(--i);/*块大小*/
}
returnpa;/*返回分配块的地址*/
}
Spacebuddy(Spacep)
{/*返回起始地址为p,块大小为pow(2,p->kval)的块的伙伴地址*/
if((p-r)%(int)pow(2,p->kval+1)==0)/*p为前块*/
returnp+(int)pow(2,p->kval);/*返回后块地址*/
else/*p为后块*/
returnp-(int)pow(2,p->kval);/*返回前块地址*/
}
voidReclaim(FreeListpav,Space*p)
{/*伙伴系统的回收算法。
将p所指的释放块回收到可利用空间表pav中*/
Spaces;
s=buddy(*p);/*伙伴块的起始地址*/
while(s>=r&&stag==0&&s->kval==(*p)->kval)/*归并伙伴块*/
{/*,伙伴块起始地址在有效范围内且伙伴块空闲并与p块等大,从链表上删除该伙伴块结点*/
if(s->rlink==s)/*链表上仅此一个结点*/
pav[s->kval].first=NULL;/*置此链表为空*/
else/*链表上不止一个结点*/
{
s->llink->rlink=s->rlink;/*前驱的后继为该结点的后继*/
s->rlink->llink=s->llink;/*后继的前驱为该结点的前驱*/
if(pav[s->kval].first==s)/*s是链表的第一个结点*/
pav[s->kval].first=s->rlink;/*修改表头指向下一个结点*/
}/*以下修改结点头部*/
if((*p-r)%(int)pow(2,(*p)->kval+1)==0)/*p为前块*/
(*p)->kval++;/*块大小加倍*/
else/*p为后块(s为前块)*/
{
s->kval=(*p)->kval+1;/*块大小加倍*/
*p=s;/*p指向新块首地址*/
}
s=buddy(*p);/*下一个伙伴块的起始地址*/
}/*以下将p插到可利用空间表中*/
(*p)->tag=0;/*设块标志为空闲*/
if(pav[(*p)->kval].first==NULL)/*该链表空*/
pav[(*p)->kval].first=(*p)->llink=(*p)->rlink=*p;/*左右指针及表头都指向自身*/
else/*该链表不空,插在表头*/
{
(*p)->rlink=pav[(*p)->kval].first;
(*p)->llink=(*p)->rlink->llink;
(*p)->rlink->llink=*p;
(*p)->llink->rlink=*p;
pav[(*p)->kval].first=*p;
}
*p=NULL;
}
voidPrint(FreeListp)
{/*输出p中所有可利用空间表*/
inti;
Spaceh;
for(i=0;i<=m;i++)
if(p[i].first)/*第i个可利用空间表不空*/
{
h=p[i].first;/*h指向链表的第一个结点的头部域(首地址)*/
do
{
printf("块的大小=%d块的首地址=%u",(int)pow(2,h->kval),h);/*输出结点信息*/
printf("块标志=%d(0:
空闲1:
占用)\n",h->tag);
h=h->rlink;/*指向下一个结点的头部域(首地址)*/
}while(h!
=p[i].first);/*没到循环链表的表尾*/
}
}
voidPrintUser(Spacep[])
{/*输出p数组所指的已分配空间*/
inti;
for(i=0;iif(p[i])/*指针不为0(指向一个占用块)*/
{
printf("占用块%d的首地址=%u",i,p[i]);/*输出结点信息*/
printf("块的大小=%d",(int)pow(2,p[i]->kval));
printf("块标志=%d(0:
空闲1:
占用)\n",p[i]->tag);
}
}
voidmain()
{
inti,n;
FreeLista;
Spaceq[N]={NULL};/*q数组为占用块的首地址*/
printf("sizeof(BYTE_b)=%um=%u(int)pow(2,m)=%u\n",sizeof(BYTE_b),m,(int)pow(2,m));
for(i=0;i<=m;i++)/*初始化a*/
{
a[i].nodesize=(int)pow(2,i);
a[i].first=NULL;
}
memset(gBuffer,0x00,256*1024);
//r=a[m].first=(BYTE_b*)malloc(a[m].nodesize*sizeof(BYTE_b));/*在最大链表中生成一个结点*/
r=a[m].first=(BYTE_b*)gBuffer;
if(r)/*生成结点成功*/
{
r->llink=r->rlink=r;/*初始化该结点*/
r->tag=0;
r->kval=m;
Print(a);
n=100;
q[0]=AllocBuddy(a,n);/*向a申请100个BYTE_b的内存(实际获得128个BYTE_b)*/
memcpy(q[0]+2,"aaaaaa",6);
printf("%s",q[0]);
printf("申请%d个字节后,可利用空间为:
\n",n);
Print(a);
PrintUser(q);
n=200;
q[1]=AllocBuddy(a,n);/*向a申请200个BYTE_b的内存(实际获得256个BYTE_b)*/
memcpy(q[1]+2,"aaaaaa",6);
printf("%s",q[0]);
printf("申请%d个字节后,可利用空间为:
\n",n);
Print(a);
PrintUser(q);
n=220;
q[2]=AllocBuddy(a,n);/*向a申请220个BYTE_b的内存(实际获得256个BYTE_b)*/
memcpy(q[2]+2,"aaaaaa",6);
printf("%s",q[0]);
printf("申请%d个字节后,可利用空间为:
\n",n);
Print(a);
PrintUser(q);
Reclaim(a,&q[1]);/*回收q[1],伙伴不空闲*/
printf("回收q[1]后,可利用空间为:
\n");
Print(a);
PrintUser(q);
Reclaim(a,&q[0]);/*回收q[0],伙伴空闲*/
printf("回收q[0]后,可利用空间为:
\n");
Print(a);
PrintUser(q);
Reclaim(a,&q[2]);/*回收q[2],伙伴空闲,生成一个大结点*/
printf("回收q[2]后,可利用空间为:
\n");
Print(a);
PrintUser(q);
}
else
printf("ERROR\n");
}
参考文献
[1]BruceEckel:
?
Java编程思想?
[M],机械工业出版社第四版
[2]严蔚敏吴伟民:
?
数据结构?
[M],清华大学出版社
[3]Andrew.T:
?
现代操作系统?
[M],机械工业出版社
[4]陈维心林小茶:
?
C++面向对象程序设计教程?
[M],清华大学出版社
[5]MarkAllenWeiss:
?
数据结构与算法分析?
[M],人民邮电出版社
心得体会
又是一次课程设计,之前经过了大二时期的课程设计之后,渐渐对课程设计有一定的了解,所以对于这次的课程设计来说,相比之前容易了不少,不过通过这次操作系统的课程设计,主要让我学习了有关信号量,内存等方面的一些问题,虽说伙伴系统对于自己来说有难度,但是通过实践调试和查找相关资料,懂得怎么样去发现错误了,学习理论课上不明白的东西。
不过,其中自己也犯了不少的错误,必须的一个一个错误和问题的解决。
再次使用C++编程,这门编程语言博大精深,这次实践课的内容和经验,让自己温习了以前所学的东西,又加强了自己和团队的交流。
每次实践课都是一个学习理论的验证,我觉得不管什么课程的实践课都必须认真对待,通过实际操作去理解理论中的抽象,那样才能真正弄懂这门课程的精髓,才能在学习中有所收获。
计算机学院课程设计辩论记录表
专业学院
计算机学院
专业
软件工程
姓名
学号
课程设计
题目
信号量控制线程同步
辩论日期
辩论时间
答
辩
提
问
及
其
回
答
记
录
教师评语