第9章、多线程编程.docx
第9举多线程编程本章0标在前两章中.读者主要学习了有关进程控制和进程间通信的开发,这些都是UnUX开发的根底.在这一章中将学习轻最线进程战程的开发,由于线程的高效性和可操作性,在大型程序开发中运用得非常.广泛,带望读者能够很好地掌握.掌握1.inux中线程的根本概念口拿握1.inUX中线程的创立及使用U掌1.inux中线在属住的设五1.J能够以立场写多蛾程程序1.1.9.1 1.inUX线程概述线程概述前面已经提到,进程是系统中程序执行和资源分配的根本单位。得个进程都拥行自己的数据段、代码段和堆枝段.这就造成了进程在进行切换等操作时挪需要彳f比拟熨杂的上下文切换等动作.为了进一步就少处理机的空转时间,支持多处理涔以及或少上下文切换开销,进程在演化中出现了另一个概念线程。它是进程内独立的一条运行路线,处理潜调度的最小单元也可以称为轻量级进程.线程可以对进程的内存空间和资源进行访问,并与同一进程中的其他线程共享.因此,级程的上下文切换的开销比创立进程小很多。同进程一样,线程也将相关的执行状态和存储变域放在线程控制表内.一个进程可以有多个线程,也就是有多个线程控制表及堆栈存放器.但却共享一个刖户地址空间.要注意的是由于线程共享了进程的资源和地址空间,因此,任何战程对系统资源的操作都会给其他规程带来影响。由此可知,多线;程中的同步是非常界要的问题。在多战程系统中,进程与进程的关系如图9所示.图9.1进程与我桂关系线程机制的分类和特性双程按照其时度拧可以分为用户级线程和核心级线程两种-(I)用户级规程。用户级战程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定.在运行时不需要特定的内核支持.在这里,操作系统往往会提供一个用户空间的税程诲,该线程际提供了践程的创立、调度和微错等功能,而内核仍然仅对进程诳行管理。如果一个进程中的某一个战程调用了一个阻塞的系统调用函数,那么该进程包括该进程中的其他所有线程也同时被阻塞.这种用户级线程的主要缺点是在一个诳程中的多个线程的调度中无法发挥多处埋器的优势.(2)轻埴奴进程。轻量级进程是内核支持的用户线程,是内核线程的一种抽象对我.每个线程拥有一个或多个轻疑被税程,而每个轻量级线程分别被绑定在一个内核战程上。内核战程。这种线程允许不同进程中的线程按照同一相时优先调度方法进行两度,这样就可以发挥多处理器的并发优势.现在大多数系统都采用用户级战程与核心级战程并存的方法。一个用户级线程Ur以对应一个或几个核心级线程,也就是“一对一”比“多对一”模型.这样既可满足多处理机系统的需要也可以最大限度地战少两度开销.使用战程机制大大加快上下文切换速度而且节省很多资源,但是因为在用户态和内核态均要实现询度管抻,所以会增加实现的更公度和引起优先级朝转的可能性.一个多线程程序的同步设计与调试也会增加程序实现的难度”9.2 1.inUX线程编程线程根本编程这里要讲的线程相关操作都是用户空间中的规程的操作.在1.inux中,一般Pthread线程库是一套通用的规程阵,是由POSIX提出的,因此具有很好的可移植性.(1)函数说明.创立规程实际上就是确定调用该线程函数的入口点,这里通例使用的函数是P1.hrea1.Crea在践程创立以后,就开始运行相关的戏程函数,在该函数运行完之后,该线程也就退出了.这也是战程退出一种方法.另一种退出线程的方法是使用的数p<hrcadxit,这是线程的主动行为.这里要注意的是,在使用线程函数时,不能K1.意使用cxit()退出函数进行H1.错处理,由于exit。的作用是使调用进程终止,往往一个进程包含多个战程,因此,在使用exi之后,该进程中的所有线程都终止了.因此,在线程中就可以使用PthEM1.eXiIo来代替进程中的cxit().由于一个进程中的多个规程是共享数据段的,因此通常在线程退出之后,退出找程所占用的资源并不会髓者战程的终止而得到拜放,正如进程之间可以用Wai1.o系统调用来同步终止并释放资源样,线程之间也有类似机制,那就是P1.hrCadJoin()函数.P1.hrCadjoin(>可以用于将当前线程拄起来等待规程的结束.这个函数是一个规程阻我的函数,调用它的函数将一直等待到被簪待的代程结束为止,当南救返回时,被等待线程的资源就被收回。前面已提到线程调用pthreadfXitO函数主动终止自身线程.但是在很多线程应用中,羟常会遇到在别的线程中要终止另一个线程的执行的何时。此时调用Pthrea1.CanCe1.o函数实现这种功能,但在被取消的线程的内部需要调用Pthtra1.scICaneC1()函数和p<hrea1.sCICanCdIyPC()的数设置自己的取消状态,例如被取消的线程接收到另一个线程的取消请求之后,是接受还是忽略这个请求:如果接受.是立刻进行终止操作还是等待某个函数的调用等.(2)函数格式。表9.1列;I;/PthrCad_crcatc()函数的谱法要点.表。PthrWUI_CrVat«0由S1.t语法要点所需央文件inc1.ude<p1.hrc;id.h>ft5!ui1.P1.hiYac1.crvaM(p1.htuad,1.thread,pciuvad.a(1.rJ4a1.1.r.void*(tstat.rcHJi)(x)id*).void*arg>>由政传入Ihrsdr规程标识符Jftaiir:(共区体设JH参见小节),为常取为NU1.1.'Urt/uutine:找程雨数的起始地址.是一个以指向vuid的指针作为警致和返H1.ft的函数指针I1.rgIi2Ma11.roinc(Kft-IaiSiSI成功20(ft出楮:返回错限码表9.2列IH了P1.hrCad_exi1.()函数的语法要点。表9.2PthrVad_e、it()函数语法妾点所格头文件Windude<Mhread.h>函数原型Voidpthrc;wi_cxit(wid*c1.va1.)Ffi8(传入位revah线和给束时的iM"(i1.Id1.HttjFfitttaP1.hreHdJoEO来获取表93列出了P1.hreadjoi110函数的语法要点。表。与pdEdJinO函效语法要点所需为文件*inc1.udc<thcad.h>in(PthrcJcin(pthrcjth.Veid*thrcnd.reium)th等待线程的标识符函数传入值thicadjCtUm:用户定义的折计,用来存储被等特级利结束时的返H1.W(不为Nu1.1.时)的散返同伙成爪0btft返回错误码ii9.4列出了PthrCa1.CanCdo函数的语法要点.表0”,Pt1.MTX1.mc*T()函数诘法要点所需头文件析IKhKfc<pchrcod.h>的致原案intphrcad-cact<p<hrcudjth)函数传入值h)要取消的线程的标识符函数返回成功I0(ftH1.ffi1.返回错误码(3)函数使用.以下实例中创立了3个战程,为了更好地描述燃程之间的并行执行,让3个战程重用同一个执行函数。每个线程都有S次循环(可以存成5个小任务),每次循环之间会随机等待I-IoS的时间意义在于模拟每个任分的到达时间是时机的,并没仔任何特定规律./*thread.c*/inc1.ude<stdio.h>inc1.ude<std1.1.b.>!inc1.ude<pthread.h>”战.程金、/每个慢程中的小任务数“小任务之间的最大defineTHE<EAD_NUMBER3!defineREPEAT_NUMBER5./defineDEIY-TIME-1.ErE1.S10.0时向何喝*/voidthrd-fnc(voidwarg)(八线程居效例程/intthrd_nuni-(int)arg;intde1.ay_ti.me三0;intcount=0;printtnread9dis8tartingnthrd_num>for<count-0;count<REPEATNUMBER;countde1.ay-ti-<intj<rand11*DE1.AY_TIME_1.EVE1.SZ<RND-MX)÷1;s1.eep(de1.ay-tme1.;rintfwtThreadW:job*dde1.ay-1.dn-zthrd_num,count,de1.ay_time);)rintf1.,Tread、dCinishednM,thrd_num>;pthread_cxit(NU1.1.);)intEoirHvoidI(pthread-tthrcad(THREAD_NUMB£R;intno-Orres;voidthrd-ret;8rand(tire(NU1.1.>);for<no-0;no<THREAD-NUMBER;no)(/A锁立多线程-/espthreadcreate(6threadnozNU1.1.,trdefuncf(void*)no);ifres!-O)(rintfIwCreatethread%dta1.1.edn,fno);exit<res);)rintf("CreatetreadssuccessXnWaitingforthreadstofinish.n->for<no-O;no<THREAD_NUMBER;no+)(等待我程结束7res=pthxead_join(threadno,fithrd_ret);if(!res)(printfI"Threadtd)oinedn,zno>)e1.se<rintf1.,Thread、d:oinfai1.ednw,no);returnO;以下是程序运行结果,可以看H1.绿个战程的运行和结束是独立与并行的。S./threadCreatetreadssuccessWaitingforthreadstofinish.ThreadOisstartingThreadThread1isstarting2isstartingThread1:jobOde1.ay.6Thread2:jobOde1.ayM6ThreadO:JobOde1.ay9Thread1:job1de1.ay6Thread2:job1de1.ayarBThreadO:job1de1.ay8Thread2:Job2de1.ayB3ThreadO:job2de1.ay3Thread2:job3de1.ayW3ThreadThread2:job42finishedde1.ay=1Thread1:Job2de1.ay10Thread1:job3de1.ay.4ThreadThread1:job41finishedde1.ay=1ThreadO:job3de1.ayB9ThreadThreadO:job4Ofinishedde1.ayB2ThreadOjoinedThread1JoinedThread2joined线程之间的I可步与互斥由干线程共享进程的资源和地址空间,因此在对这些资源进行操作时,必须考虑到线程间资源访问的同步与互斥问遨.这里主要介绍POSIX中两种线程同步机制,分别为互斥领和佶号I七这两个同步机制可以互相通过调用对方来实现,但互斥锁更适合用于同时可用的资源是植的情况;信号址更适合用于同时可用的资源为各个的情况,1 .互斥锁线程控制(I)函数说明。互斥锁是用一种简单的加锁方法来控制对共享资源的原子操作.这个互斥恢只有两种状态,也就是上锁和解领.可以把互斥惯看作某种意义上的全局变Jib在同一时刻只能有个线程掌握某个互斥锁.拥有上锁状态的畿程能野对共享资源进行操作.假设其他践程希望上钺一个己经被上锁的互斥锁,那么该线程就会挂起,直到上锁的战程择放掉互斥锁为止.可以说,这把互斥慎保证让每个线程对共享资源按顺序进行原子操作.互斥镇机制主要包括下面的根本函数, 互斥锁初始化:P1.hrea(1.1.nUteXJni1() 互斥锁上帆PthrCad_mUtex_1.ock(> 互斥锁判断上锁:pihrcad_inutcx_try1.ock() 互斥锁接钺:PIhrea(1.mUteX-UnIOCko 消除互斥锁:PIhrVM1.mU1.e1.dCSUUy()其中,互斥锁可以分为快速互斥领、递归互斥锁和检借互斥快.这3种锁的区别主要在于其他未占有互斥锁的线程在科里得到互斥救时是否需要阻塞等待,快速锁是指调用线程会阻塞口.至拥有互斥锁的城程解锁为止。递归互斥锁能助成功地返回,并且增加调用城程在互斥上加锁的次数,而检惜互斥领那么为快速互斥锁的非阻塞版本.它会立即返P1.并返I可一个错误信息.默认属性为快速互斥锁.(2)函数格式。表9.5列出了PIhread_muiex_ini1.()函数的语法要点.所;头文件函数旗型函ft传入Kit*inchnfc<p(hrcod.h>表OJiPU1.E<1.uutcJ"1.t()函圾语法要点mutex:死耳锁intpchrcad-mwxjni(pthread.mutcxjfmwx.constP1.hrriK1.mUKXimjtrmitcxattr)Muw.a(trPTHREADMUTCXJNITIA1.IZER1创立.快速肛斥搐PTHRMe_recurS1.VEJMUTexJneaijzfrnp:创立述互斥锁PTHRI1.AD_ERRORCHECK_MUTEX_INIT1A1.IZER_NPf创立核M互斥钺成功:0出曲运回精返码&9.6列出了PthrCadjnutcxOCko等函数的语法要点.«9.0所需头文件南OdUdC<p<1.wcad.h>的数炭型iniPIh1.VadnKnCX1.ock(pt1.rvadnu1.ex.1."mutexJimthread.muicx.ryock<pi1.wcad.nwicx.itmucx.)incpihread_muicx-un1.oek<pthfcad_nwicx_ttmucx.)impthmu1.mutcx_dcrtmy(p<hrc;id_mu1.cx_tmuteKj的数传入ffimutex?i.斥国由放返国值成功?OPthrC"<1.uut<J<MkO等淘数语法要点(3)使用实例.下面的实例是在小节例如代码的根底上刷加互斥领功能,实现原本独立与无序的多个战程能铭按IWi序执行.*thread-mte×.c*/inc1.ude<scdo.h>inc1.ude<std1.ib.h>inc1.ude<pthread.h>!defineTHREAjNUMBER3*伎程效fcdefineREPEAJNUMBER3a好个蝶程的小任务做/defineDE1.AY_TIME_1.EVE1.S10.0小任务之间的最大时向何隔夫/PthrOadBUtQX_七nuto×voidthrd-func(voidrg)(intthrd_num-(int)arg;intd1.ay-tim=Orcount=0;intres;/,互斥愫上M/rea=pthread_mutex_1.ock(&mutcx);if(res>printfInThread、d1.ockfai1.edn,thrd-nm>pthread-exit(NU1.1.);)printfIwThreadZdis三tartingnw,thrd_num>for<cont=0;count<REPEAJNUMBER;count+*)<de1.ay3<1.nt<rand(>DE1.AY-TIME-1.EVE1.S/<RAND_MAX)÷1;s1.p(de1.ay-tm1.;printfwchreadtd:job¼dde1.ay-¼dn"rthrjnum.count,de1.aytime);)printf1.n7hreaddFiTnshgdn"thrd-nm>pthread_exit(NU1.1.);)intmain(void>(thread-tthreadTHREADJiUMBER;intno-Orres;voidthrd_ret;Sgnd1.CiJT总(NUU4I;卜互斥就初始化7pthread_nutex_init(6mutex.NU1.1.);for<no-0;no<THREAD_NUMBER;no÷+1.(res-pthreadMCreat«(&thead(no/NU1.1.,thrdefunc,(void*)no);ifres!三0)(printfI-Createthread、dta1.1.ednMfno);exit(res);)printfIwCreatetreadsauccesaXnWaitingforthreadstofinish.nw>for(no-0;no<THREAD_NUMBER;no÷+)(IT-pthreadM)oin(thceadno,Sthrdjrot);if(!res)(printf("Threaddjoinednwzno);)e1.se(printf<"Thread*dJoinfai1.ednw,no);)*互斥锁W<fiVpthreadnwte×vun1.ock(4nute×);)Pthreadrftutexdestroy(Amutex);returnO;)该实例的运行结果如下所示,这里3个线程之间的运行联序跟创立规程的顺序相同.$./thread_njutexCreatetreadssuccesswaitingforthreadstotinsh.ThreadOisstartingThreadO:jobOde1.ay1.7ThreadO:job1d1.e>y=7ThreadO:Job2de1.ay-6ThreadOfinishedThreadOjoinedThread1isstartingThread1:JobOde1.ay-3Thread1:Job1de1.ay-5Thread1:job2de1.ay-10Thread1finishedThread1joinedThread2isstartingThread2:job0de1.ay三6Thread2:job1de1.ay=10Thread2:job2de1.ay-3Thread2finishedThread2joined2.信号量战程控制(1)信号It说明.在第8章中已经讲到,信号吊也就是操作系统中所用到的PV原子操作,它广泛用于进程或线程间的同步与互斥.信号盘本质上是一个非负的整数计数器,它被用来控制对公共资源的访问.这里先於简单究习一下PV烷子悚作的工作原理.PV原子操作是对整数计数器信号fitscm的操作.一次P操作使Sem减、而一次V愫作使Sem加一。进程(或战程)根据信号做的值来判断是否对公扶资源具有访问权限,当信号量SCm的值大于等于零时,该进程(或线程)具有公共资源的访问权限:相反.当信号城Sem的值小于零时,该进程(或线程)就将阻东直到信号录Sem的值大于等于O为止.PV原子操作主要用于进程或线程间的同步和互斥这两种典型情况.假设用于互斥,几个进程(或线程)往往只设度一个信号量scm.它们的操作流程如图9.2所示.当信号址用于同步掾作时,往往会设置多个信号I丸并安悻不同的初始值来实现它们之间的顺序执行,它们的操作流程如图9.3所示。图9.2信号Irt互斥掇作图9.3%号眼同步撞作函数说明。1.inux实现了POS1.X的无名信号量,主要用于线程间的互斥与同步。这里主要介绍几个常见的数.,scni_init()用于创立一个信号量,并初始化它的值. Sem-Wai1.o和Sem_tIyWai1.()都相当于P悚作,在信号收大于零时它们都能将信号量的值减-,两者的区别在于假设信号附小于零时,scm_Waho将会阻塞进程,而SemIyWaiK)那么会立即返回. Sem_POM()相当于V操作,它将信号量的他加一同时发出信号来唤醒等待的进程.scmJetVaIUCO用于得到信号量的值.scni_dcstroyO用于删除仿号t,函数格式。表9.7列出了Sem_ini1.()函数的语法要点.«0.7Wmnit。语数语法要点*;i】.iMneIudtr<semap1.u><v.h.>函数1.51m1.x%em.in1.x>1.ured.utgnedtfva1.ueI论数传入.m:伯号量指计值PSiUwb决定信号北徙否在儿个进程间共也由于IIMUnU还没仃实现进程间共享倡守.所以这个值只饯好取0.就表示这个信“此站当前进程的局部信“成VaI岭信跳初始化值函放返回(A成功:0HIM:-1表9.8列出了SCm_Waito等函数的语法要点.表。“S函数语法要点所;S头文件#inc1.ude<p<hrcad.h>ftS5!intscm.wai1.(scm.t4scm)intscmjr>'wait(scmj*scm)int*emjxM(scmJJCm)iniM11n-et¼3ue(semj*em)iniSen1.dCS1.KJy(SCm飞CJn)嫉数传入值SCm:信号他指计由政返回fft成功10出错I-I(4)使U实例。在前面已经通过互斥锁同步机制实现广多线程的顺序执行.下向的例子是用信号6同步机制实现3个税程之间的有序执行,只是执行顺序是跟创汇线程的顺序相反.*thr9ad-sem.cinc1.ude<atdio.h>inc1.udetstd1.1.b.h>inc1.ude<pthread.h>*inc1.de<smaphore.h>段程敦/*每个圾程中的/小任务之间的最大defineTHREAD-NUMBER3defineREPEAT_NUMBER3小任务数/defineDE1.Y-iME-1.E,E1.S10.0时何何网/sem_t三em(THREADeNUMBERJ;voidthrd-func(void<rg)(mtthrd-nm-(Int)arg;intde1.aytime-0;intcount=0;进行P盘作»/sam_wait(6som(thrd_nxan>printfInThread、disstartingnM,thrd-nm>for<count-0;count<REPEAT_NUMBER;cont÷t)(de1.ay-ti-<int<rand(>*DE1.Y-TIME-1.EVE1.S/<RAND_MAX|)*1;a1.eep(de1.ay-tme1.;rintf(wtThread¼d:job¼dde1.ay-thrd_numfuntrde1.ay_time);)rintt7r.readdC1.ni8hedn,thrd-nm);pthread_cxit(NU1.1.);)intmain(void1.(PthreadtthreadITHREADNUMBER;intno1.0rre三;voidthrd-ret;stand(tiJ(NU1.1.I;for(no-0;no<THREADeNUMBER;11O¼÷)<5em_init(sem1.no)rOr0>res-pthreadcreatethreadnozNU1.1.,thrd_func,(void*)no);if(res!-0)<rintf!-Createthread%dta1.1.edn,rno);exit<res);)rintf(,CreatetreadsSucceaaXnWaitingforthreadstofinish.nt>对立后创立的战程的信号费遂行V操作78emj>8t(48m(THREAD-NUMBER-1J);for<no-THREAD_NUMBER-1;no>-0;no->(res-pthrcad_join(threadno1.f4thrd_retI;if11resIprintf(wThreaddjoinedn,no>)e1.se(printf(,Treaddjoinfai1.ednwfno);)近行V操作7三em-po三t(fcaem(no+THMAD-NUMBER-1)%THREAD_NUMBER);)for<no=0;no<THREADeNUMBER;no÷+1.(*fir*/sm_dostroy(ftsm(no);)return0;该程序运行结果如下所示:$./thread_semCreatetreadssuccessWaitingforthreadstofinish.Thread2isstartingThread2:job0de1.ay=9Thread2:JOb1de1.ay-5Thread2:job2de1.ay-10Thread2finishedThread2joinedThread1isstartingThread1:job0de1.ay-7Thread1:job1de1.ay-4Thread1:job2de1.ay=4Thread1finishedThread1joinedThread0isstartingThread0:job0de1.ay=10Thread0:job1de1.ay-8Thread0:job2de1.ay-9Thread0finishedThread0joined线程属性(1)函数说明。p<hrcad_creatcO函数的第二个参数(Pthread_attr_t*attr)表示线程的属性.在上一个实例中,将该的设为NU1.1.,也就是采用默认他性,现程的多项属性都是可以更改的.这些展性主要包括绑定闽性、别点属性、加栈地址、堆栈大小以及优先级。其中系统欣认的属性为非绑定、非别离、缺省IM的堆栈以及1.j父进程同样蝮别的优先级.下面首先M绢定机件和别高屈性的根本做念进行讲解.绑定屈性。附面已经提到,1.inUX中采用“一对一”的战程机制,也就是一个用户线程对应一个内核线程.绑定属性就是指一个用户线程同定地分配给一个内核线程,因为CPU时间片的调度是面向内核线程1也就是轻量级进程)的,因此具有绑定属性的跷程可以保证在需要的时候总有一个内核线程与之时应.而与之对应的作绑定属性就是指用户找程和内核线程的关系不是始终固定的,而是由系统来控制分配的.别离属性,别岗属性是用来决定一个戌程以什么样的方式来终止自己.在非别离情况下,当一个战程结束时,它所占用的系统资源并没有被择放.也就是没有真正的终止.只有当p<hrcadjOinO函数返回时,创立的线程才能择放自己占用的系统资源.而在别疡闻性情况下,一个线程结束时立即林放它所占有的系统资源。这里要注逾:的一点是,如果设置一个线程的别离属性,而这个战程运行又非常快,那么它很可能在Pthrcat1.CmatC()函数返回之前就终止了.它终止以后就可能将线程号和系统资源移交给其他的线程使用,这时调用p<hNa1.CreatCO的战程就得到了错误的战程号.这些属性的设置都是通过特定的函数来完成的,通常首先调用Pthrca1.1.aUJiniH)函数进行初始化,之后再调用相应的属性设置函数,最后网用p*hrca1.aUukSgyo函数对分配的璃性结肉指针进行消埋和回收.设议绑定属性的函数为IXhga1.aUJSeucope(),设置线程别离M性的场数为P1.hrea1.aUr_SeIde1.aChWa1.e(),设置线程优先端的相关函%为Pthrcad_attr_gctschedpamm(>(获取线程优先级)和P1.hrCa1.aUJsCKehCdParamo(设置线程优先级).在设置完这些属性后,就可以调用P1.hreadfea1.e。函数来创立战程了»(2)函数格式。表9.9列出了PthrCad_attr_iniK)函数的谱法要点。表9.0PIhrvj1.n1.t()函数语法要点所霜头文件ti1.u(fc<p<hrcad.h>intpthfcad_attr_init(pihread_atu_i*a»r)intno_0,res;void,thrd_ret;arand(ti11e(NU1.1.>1.;初始化栽程的性雨象/res=pthroad_attr_init(iattr);ifIres!=0)(rintf("Createattributefa1.1.edn,>exit<res);)设置及程绑定属性7res=pthrcad_attr_actscopc(fiattr,PTHREAD_SCOPE_SYSTEM);/设置线程别离属性/r0s+=pthrad-attr-SQtdetachetato(&attrrPTHREAD-CREATE-DETACHED);ifres!-0),printfIwSettingattributefai1.edn,);exit<res;)res=pthreadierQato(6thread,6attrrthrd-fncrNU1.1.);ifIres!=0)<printf(wCreatethreadfai1.e<in>exit<resI;)株放段程属性对象/pthread_attradestroy(4attr);printf(*,Createtread三ucces三n,>whi1.e(!finishf1.ag><printf("Waitingforthreadtofinish.n,>s1.e«p(2>)return0;)接下来可以在战程运行前后使用Free”命令我看内存的使用情况,以下是运行结果:S./threadattrCreatetreadsuccesswaitingforthreadtofinish.ThreadisstartingWaitingrotthreadtofinish.Thread:job0de1.ay三3Waitingforthreadtofinish.Thread:job1de1.ay-2Waitingforthreadtofinish.Waitingforthreadtofinish.Kaitingforthreadtofinish.waitingforthreadtofinish.Thread:job2de1.ay-9Threadfinished*程序运行之葭/Sfreetota1.usedfreesharedbufferscachedItem:2S55561919406361610586461360-/buffers/cache:124716130840Swap:3774B818352359136/,程序运行之中*/$treetota1.usedfreesharedbufferscached快m:2555561919486360810588861336-/4buffera/cache:124724130832Swap:3774818352359136/a程序运行之后*/$freetota1.usedfreesharedbufferscachedMem:2S55561919406361610590461320-÷buffera/cache:124716130840Swap:3774B818352359136可以看到,线程在运行结束后就收【可了系统费源,并释放内存。9.3 实验内容一“生产者消费者”实验1 .实验目的“生产者消费者”问SS是一个著名的同时性编程向区的集合,通过学习羟典的“生产者消费者”问胭的实验,读者可以进一步熟悉1.inUX中的多线程编程,井旦掌押用信号埴处理城程间的同步和互斥问题.2 .实验内容“生产者一消费者”问融描述如下.有一个有限缓冲区和两个税程:生产者和消费齐.他们分别不停胞把产品放入缓冲区和从缓冲区中泉走产品。一个生产者在覆冲区满的时候必须等恃,一个消费者在缓冲区空的时候也必须等待,另外,因为援冲区是临界资源.所以生产者和消费者之间必须互斥执行.它们之间的关系如图9.4所示.图9.4生产齐消优者问题描述这里要求使用有名管道来模拟有限辍冲区,并且使用信号量来斛决生产者一消费者”问题中的同步和互斥问题。3 .实验步骤(I)信号fit的考虑.这里使用3个信号JR,其中两个信号fitavai1.和Ai1.1.分别用于解决生产者和消费者跷程之间的同步问题,muiex是用于这两个规程之间的互斥问鹿,其中avai1.衣示有界域冲区中的空单元数,初始能为N;fu1.1.表示有界缓冲区中非空单元数,初始值为0:mutex是互斥信号量初始值为1.(2)画出流程图.本实验流程图如图9,5所示。IS9.5”生产者一消些衣”实抬充程图编写代码本实木的代码中采用的有界缓冲区拥有3个单元,每个单元为5个字节,为了尽Jft友达每个信号量的意义.在程序中生产过程和消费过程是随机(采取05s的随机时间间隔)进行的,而且