下载

2下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 1492409

1492409.doc

1492409

mingche_god
2018-09-09 0人阅读 举报 0 0 0 暂无简介

简介:本文档为《1492409doc》,可适用于IT/计算机领域

║嵌入式应用程序开发详解第章多线程编程║第章多线程编程本章目标  在前两章中读者主要学习了有关进程控制和进程间通信的开发这些都是Linux中开发的基础。在这一章中将学习轻量级进程线程的开发由于线程的高效性和可操作性在大型程序开发中运用得非常广泛希望读者能够很好地掌握。掌握Linux中线程的基本概念掌握Linux中线程的创建及使用掌握Linux中线程属性的设置能够独立编写多线程程序能够处理多线程中的变量问题能够处理多线程中的同步文件Linux下线程概述线程概述前面已经提到进程是系统中程序执行和资源分配的基本单位。每个进程都拥有自己的数据段、代码段和堆栈段这就造成了进程在进行切换等操作时都需要有比较负责的上下文切换等动作。为了进一步减少处理机的空转时间支持多处理器和减少上下文切换开销进程在演化中出现了另一个概念线程。它是一个进程内的基本调度单位也可以称为轻量级进程。线程是在共享内存空间中并发的多道执行路径它们共享一个进程的资源如文件描述和信号处理。因此大大减少了上下文切换的开销。同进程一样线程也将相关的变量值放在线程控制表内。一个进程可以有多个线程也就是有多个线程控制表及堆栈寄存器但却共享一个用户地址空间。要注意的是由于线程共享了进程的资源和地址空间因此任何线程对系统资源的操作都会给其他线程带来影响因此多线程中的同步就是非常重要的问题了。在多线程系统中进程与进程的关系如表所示。图进程与线程关系线程分类线程按照其调度者可以分为用户级线程和核心级线程两种。()用户级线程用户级线程主要解决的是上下文切换的问题它的调度算法和调度过程全部由用户自行选择决定在运行时不需要特定的内核支持。在这里操作系统往往会提供一个用户空间的线程库该线程库提供了线程的创建、调度、撤销等功能而内核仍然仅对进程进行管理。如果一个进程中的某一个线程调用了一个阻塞的系统调用那么该进程包括该进程中的其他所有线程也同时被阻塞。这种用户级线程的主要缺点是在一个进程中的多个线程的调度中无法发挥多处理器的优势。()核心级线程这种线程允许不同进程中的线程按照同一相对优先调度方法进行调度这样就可以发挥多处理器的并发优势。现在大多数系统都采用用户级线程与核心级线程并存的方法。一个用户级线程可以对应一个或几个核心级线程也就是“一对一”或“多对一”模型。这样既可满足多处理机系统的需要也可以最大限度地减少调度开销。Linux线程技术的发展在Linux中线程技术也经过了一代代的发展过程。在Linux内核中并不存在真正意义上的线程。当时Linux中常用的线程pthread实际上是通过进程来模拟的也就是说Linux中的线程也是通过fork创建的“轻”进程并且线程的个数也很有限最多只能有个进程线程同时运行。Linux内核消除了这个线程个数的限制并且允许在系统运行中动态地调整进程数上限。当时采用的是LinuxThread线程库它对应的线程模型是“一对一”线程模型也就是一个用户级线程对应一个内核线程而线程之间的管理在内核外函数库中实现。这种线程模型得到了广泛应用。但是LinuxThread也由于Linux内核的限制以及实现难度等原因并不是完全与POSIX兼容。另外它的进程ID、信号处理、线程总数、同步等各方面都还有诸多的问题。为了解决以上问题在Linux内核中进程调度通过重新编写删除了以前版本中效率不高的算法。内核线程框架也被重新编写开始使用NPTL(NativePOSIXThreadLibrary)线程库。这个线程库有以下几点设计目标:POSIX兼容性、多处理器结构的应用、低启动开销、低链接开销、与LinuxThreads应用的二进制兼容性、软硬件的可扩展能力、与C集成等。这一切都使得Linux内核的线程机制更加完备能够更好地完成其设计目标。与LinuxThreads不同NPTL没有使用管理线程核心线程的管理直接放在核内进行这也带了性能的优化。由于NPTL仍然采用∶的线程模型NPTL仍然不是POSIX完全兼容的但就性能而言相对LinuxThreads已经有很大程度上的改进。Linux线程实现线程基本操作这里要讲的线程相关操作都是用户空间线程的操作。在Linux中一般Pthread线程库是一套通用的线程库是由POSIX提出的因此具有很好的可移植性。.线程创建和退出()函数说明创建线程实际上就是确定调用该线程函数的入口点这里通常使用的函数是pthreadcreate。在线程创建以后就开始运行相关的线程函数在该函数运行完之后该线程也就退出了这也是线程退出一种方法。另一种退出线程的方法是使用函数pthreadexit这是线程的主动行为。这里要注意的是在使用线程函数时不能随意使用exit退出函数进行出错处理由于exit的作用是使调用进程终止往往一个进程包含多个线程因此在使用exit之后该进程中的所有线程都终止了。因此在线程中就可以使用pthreadexit来代替进程中的exit。由于一个进程中的多个线程是共享数据段的因此通常在线程退出之后退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样线程之间也有类似机制那就是pthreadjoin()函数。pthreadjoin可以用于将当前线程挂起等待线程的结束。这个函数是一个线程阻塞的函数调用它的函数将一直等待到被等待的线程结束为止当函数返回时被等待线程的资源就被收回。()函数格式表列出了pthreadcreate函数的语法要点。表pthreadcreate函数语法要点所需头文件#include<pthreadh>函数原型intpthreadcreate((pthreadt*thread,pthreadattrt*attr,void*(*startroutine)(void*),void*arg))函数传入值thread:线程标识符attr:线程属性设置(具体设定在会进行讲解)续表startroutine:线程函数的起始地址arg:传递给startroutine的参数函数返回值成功:出错:(表列出了pthreadexit函数的语法要点。表pthreadexit函数语法要点所需头文件#include<pthreadh>函数原型voidpthreadexit(void*retval)函数传入值Retval:pthreadexit()调用者线程的返回值可由其他函数如pthreadjoin来检索获取表列出了pthreadjoin函数的语法要点。表pthreadjoin函数语法要点所需头文件#include<pthreadh>函数原型intpthreadjoin((pthreadtth,void**threadreturn))函数传入值th:等待线程的标识符threadreturn:用户定义的指针用来存储被等待线程的返回值(不为时)函数返回值成功:出错:(()函数使用以下实例中创建了两个线程其中第一个线程是在程序运行到中途时调用pthreadexit函数退出第二个线程正常运行退出。在主线程中收集这两个线程的退出信息并释放资源。从这个实例中可以看出这两个线程是并发运行的。*threadc*#include<stdioh>#include<pthreadh>*线程一*voidthread(void){inti=for(i=i<i){printf("Thisisapthreadn")if(i==)pthreadexit()sleep()}}*线程二*voidthread(void){intifor(i=i<i)printf("Thisisapthreadn")pthreadexit()}intmain(void){pthreadtid,idinti,ret*创建线程一*ret=pthreadcreate(id,,(void*)thread,)if(ret!=){printf("Createpthreaderror!n")exit()}*创建线程二*ret=pthreadcreate(id,,(void*)thread,)if(ret!=){printf("Createpthreaderror!n")exit()}*等待线程结束*pthreadjoin(id,)pthreadjoin(id,)exit()}以下是程序运行结果:root(none)tmp#threadThisisapthreadThisisapthreadThisisapthreadThisisapthreadThisisapthreadThisisapthread.修改线程属性()函数说明读者是否还记得pthreadcreate函数的第二个参数线程的属性。在上一个实例中将该值设为也就是采用默认属性线程的多项属性都是可以更改的。这些属性主要包括绑定属性、分离属性、堆栈地址、堆栈大小、优先级。其中系统默认的属性为非绑定、非分离、缺省M的堆栈、与父进程同样级别的优先级。下面首先对绑定属性和分离属性的基本概念进行讲解。(绑定属性前面已经提到Linux中采用“一对一”的线程机制也就是一个用户线程对应一个内核线程。绑定属性就是指一个用户线程固定地分配给一个内核线程因为CPU时间片的调度是面向内核线程(也就是轻量级进程)的因此具有绑定属性的线程可以保证在需要的时候总有一个内核线程与之对应。而与之相对的非绑定属性就是指用户线程和内核线程的关系不是始终固定的而是由系统来控制分配的。(分离属性分离属性是用来决定一个线程以什么样的方式来终止自己。在非分离情况下当一个线程结束时它所占用的系统资源并没有被释放也就是没有真正的终止。只有当pthreadjoin()函数返回时创建的线程才能释放自己占用的系统资源。而在分离属性情况下一个线程结束时立即释放它所占有的系统资源。这里要注意的一点是如果设置一个线程的分离属性而这个线程运行又非常快那么它很可能在pthreadcreate函数返回之前就终止了它终止以后就可能将线程号和系统资源移交给其他的线程使用这时调用pthreadcreate的线程就得到了错误的线程号。这些属性的设置都是通过一定的函数来完成的通常首先调用pthreadattrinit函数进行初始化之后再调用相应的属性设置函数。设置绑定属性的函数为pthreadattrsetscope设置线程分离属性的函数为pthreadattrsetdetachstate设置线程优先级的相关函数为pthreadattrgetschedparam(获取线程优先级)和pthreadattrsetschedparam(设置线程优先级)。在设置完这些属性后就可以调用pthreadcreate函数来创建线程了。()函数格式表列出了pthreadattrinit函数的语法要点。表pthreadattrinit函数语法要点所需头文件#include<pthreadh>函数原型intpthreadattrinit(pthreadattrt*attr)函数传入值attr:线程属性函数返回值成功:出错:(表列出了pthreadattrsetscope函数的语法要点。表pthreadattrsetscope函数语法要点所需头文件#include<pthreadh>函数原型intpthreadattrsetscope(pthreadattrt*attr,intscope)函数传入值attr:线程属性scopePTHREADSCOPESYSTEM:绑定PTHREADSCOPEPROCESS:非绑定函数返回值成功:出错:(表列出了pthreadattrsetdetachstate函数的语法要点。表pthreadattrsetdetachstate函数语法要点所需头文件#include<pthreadh>函数原型intpthreadattrsetscope(pthreadattrt*attr,intdetachstate)函数传入值attr:线程属性detachstatePTHREADCREATEDETACHED:分离PTHREADCREATEJOINABLE:非分离函数返回值成功:出错:(表列出了pthreadattrgetschedparam函数的语法要点。表pthreadattrgetschedparam函数语法要点所需头文件#include<pthreadh>函数原型intpthreadattrgetschedparam(pthreadattrt*attr,structschedparam*param)函数传入值attr:线程属性param:线程优先级函数返回值成功:出错:(表列出了pthreadattrsetschedparam函数的语法要点。表pthreadattrsetschedparam函数语法要点所需头文件#include<pthreadh>函数原型intpthreadattrsetschedparam(pthreadattrt*attr,structschedparam*param)函数传入值attr:线程属性param:线程优先级函数返回值成功:出错:(.使用实例该实例将上一节中的第一个线程设置为分离属性并将第二个线程设置为始终运行状态这样就可以在第二个线程运行过程中查看内存值的变化。其源代码如下所示:*pthreadc*#include<stdioh>#include<pthreadh>#include<timeh>*线程一*voidthread(void){inti=for(i=i<i){printf("Thisisapthreadn")if(i==)pthreadexit()sleep()}}*线程二*voidthread(void){intiwhile(){for(i=i<i)printf("Thisisapthreadn")sleep()}pthreadexit()}intmain(void){pthreadtid,idinti,retpthreadattrtattr*初始化线程*pthreadattrinit(attr)*设置线程绑定属性*pthreadattrsetscope(attr,PTHREADSCOPESYSTEM)*设置线程分离属性*pthreadattrsetdetachstate(attr,PTHREADCREATEDETACHED)*创建线程*ret=pthreadcreate(id,attr,(void*)thread,)if(ret!=){printf("Createpthreaderror!n")exit()}ret=pthreadcreate(id,,(void*)thread,)if(ret!=){printf("Createpthreaderror!n")exit()}pthreadjoin(id,)return()}接下来可以在线程一运行前后使用“free”命令查看内存使用情况。以下是运行结果:root(none)tmp#threadThisisapthreadThisisapthreadThisisapthreadThisisapthreadThisisapthreadThisisapthreadThisisapthreadThisisapthreadThisisapthreadThisisapthread…rootwwwyul#freetotalusedfreesharedbufferscachedMem:bufferscache:Swap:rootwwwyul#freetotalusedfreesharedbufferscachedMem:bufferscache:Swap:rootwwwyul#freetotalusedfreesharedbufferscachedMem:bufferscache:Swap:可以看到线程一在运行结束后就收回了系统资源释放了内存。线程访问控制由于线程共享进程的资源和地址空间因此在对这些资源进行操作时必须考虑到线程间资源访问的惟一性问题这里主要介绍POSIX中线程同步的方法主要有互斥锁和信号量的方式。.mutex互斥锁线程控制()函数说明mutex是一种简单的加锁的方法来控制对共享资源的存取。这个互斥锁只有两种状态也就是上锁和解锁可以把互斥锁看作某种意义上的全局变量。在同一时刻只能有一个线程掌握某个互斥上的锁拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望上锁一个已经上锁了的互斥锁则该线程就会挂起直到上锁的线程释放掉互斥锁为止。可以说这把互斥锁使得共享资源按序在各个线程中操作。互斥锁的操作主要包括以下几个步骤。(互斥锁初始化:pthreadmutexinit(互斥锁上锁:pthreadmutexlock(互斥锁判断上锁:pthreadmutextrylock(互斥锁接锁:pthreadmutexunlock(消除互斥锁:pthreadmutexdestroy其中互斥锁可以分为快速互斥锁、递归互斥锁和检错互斥锁。这三种锁的区别主要在于其他未占有互斥锁的线程在希望得到互斥锁时的是否需要阻塞等待。快速锁是指调用线程会阻塞直至拥有互斥锁的线程解锁为止。递归互斥锁能够成功地返回并且增加调用线程在互斥上加锁的次数而检错互斥锁则为快速互斥锁的非阻塞版本它会立即返回并返回一个错误信息。()函数格式表列出了pthreadmutexinit函数的语法要点。表pthreadmutexinit函数语法要点所需头文件#include<pthreadh>函数原型intpthreadmutexinit(pthreadmutext*mutex,constpthreadmutexattrt*mutexattr)函数传入值Mutex:互斥锁续表函数传入值MutexattrPTHREADMUTEXINITIALIZER:创建快速互斥锁PTHREADRECURSIVEMUTEXINITIALIZERNP:创建递归互斥锁PTHREADERRORCHECKMUTEXINITIALIZERNP:创建检错互斥锁函数返回值成功:出错:(表列出了pthreadmutexlock等函数的语法要点。表pthreadmutexlock等函数语法要点所需头文件#include<pthreadh>函数原型intpthreadmutexlock(pthreadmutext*mutex,)intpthreadmutextrylock(pthreadmutext*mutex,)intpthreadmutexunlock(pthreadmutext*mutex,)intpthreadmutexdestroy(pthreadmutext*mutex,)函数传入值Mutex:互斥锁函数返回值成功:出错:(()使用实例该实例使用互斥锁来实现对变量lockvar的加一、打印操作。*mutexc*#include<stdioh>#include<stdlibh>#include<unistdh>#include<pthreadh>#include<errnoh>pthreadmutextmutex=PTHREADMUTEXINITIALIZERintlockvartimetendtimevoidpthread(void*arg)voidpthread(void*arg)intmain(intargc,char*argv){pthreadtid,idpthreadtmonthidintretendtime=time()*互斥锁初始化*pthreadmutexinit(mutex,)*创建两个线程*ret=pthreadcreate(id,,(void*)pthread,)if(ret!=)perror("pthreadcread")ret=pthreadcreate(id,,(void*)pthread,)if(ret!=)perror("pthreadcread")pthreadjoin(id,)pthreadjoin(id,)exit()}voidpthread(void*arg){intiwhile(time()<endtime){*互斥锁上锁*if(pthreadmutexlock(mutex)!=){perror("pthreadmutexlock")}elseprintf("pthread:pthreadlockthevariablen")for(i=i<i){sleep()lockvar}*互斥锁接锁*if(pthreadmutexunlock(mutex)!=){perror("pthreadmutexunlock")}elseprintf("pthread:pthreadunlockthevariablen")sleep()}}voidpthread(void*arg){intnolock=intretwhile(time()<endtime){*测试互斥锁*ret=pthreadmutextrylock(mutex)if(ret==EBUSY)printf("pthread:thevariableislockedbypthreadn")else{if(ret!=){perror("pthreadmutextrylock")exit()}elseprintf("pthread:pthreadgotlockThevariableisdn",lockvar)*互斥锁接锁*if(pthreadmutexunlock(mutex)!=){perror("pthreadmutexunlock")}elseprintf("pthread:pthreadunlockthevariablen")}sleep()}}该实例的运行结果如下所示:root(none)tmp#mutexpthread:pthreadlockthevariablepthread:thevariableislockedbypthreadpthread:pthreadunlockthevariablepthread:pthreadgotlockThevariableispthread:pthreadunlockthevariablepthread:pthreadlockthevariablepthread:pthreadunlockthevariablepthread:pthreadgotlockThevariableispthread:pthreadunlockthevariablepthread:pthreadlockthevariable….信号量线程控制()信号量说明在第章中已经讲到信号量也就是操作系统中所用到的PV原语它广泛用于进程或线程间的同步与互斥。信号量本质上是一个非负的整数计数器它被用来控制对公共资源的访问。这里先来简单复习一下PV原语的工作原理。PV原语是对整数计数器信号量sem的操作。一次P操作使sem减一而一次V操作使sem加一。进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量sem的值大于等于零时该进程(或线程)具有公共资源的访问权限相反当信号量sem的值小于零时该进程(或线程)就将阻塞直到信号量sem的值大于等于为止。PV原语主要用于进程或线程间的同步和互斥这两种典型情况。若用于互斥几个进程(或线程)往往只设置一个信号量sem它们的操作流程如图所示。当信号量用于同步操作时往往会设置多个信号量并安排不同的初始值来实现它们之间的顺序执行它们的操作流程如图所示。图信号量互斥操作图信号量同步操作()函数说明Linux实现了POSIX的无名信号量主要用于线程间的互斥同步。这里主要介绍几个常见函数。(seminit用于创建一个信号量并能初始化它的值。(semwait和semtrywait相当于P操作它们都能将信号量的值减一两者的区别在于若信号量小于零时semwait将会阻塞进程而semtrywait则会立即返回。(sempost相当于V操作它将信号量的值加一同时发出信号唤醒等待的进程。(semgetvalue用于得到信号量的值。(semdestroy用于删除信号量。()函数格式表列出了seminit函数的语法要点。表seminit函数语法要点所需头文件#include<semaphoreh>函数原型intseminit(semt*sem,intpshared,unsignedintvalue)函数传入值sem:信号量pshared:决定信号量能否在几个进程间共享。由于目前Linux还没有实现进程间共享信号量所以这个值只能够取value:信号量初始化值函数返回值成功:出错:(表列出了semwait等函数的语法要点。表semwait等函数语法要点所需头文件#include<pthreadh>函数原型intsemwait(semt*sem)intsemtrywait(semt*sem)intsempost(semt*sem)intsemgetvalue(semt*sem)intsemdestroy(semt*sem)函数传入值sem:信号量函数返回值成功:出错:(()使用实例下面实例使用信号量实现了上一实例中对lockvar的操作在这里使用的是互斥操作也就是只使用一个信号量来实现。代码如下所示:*semmutexc*#include<stdioh>#include<stdlibh>#include<unistdh>#include<pthreadh>#include<errnoh>#include<sysipch>#include<semaphoreh>intlockvartimetendtimesemtsemvoidpthread(void*arg)voidpthread(void*arg)intmain(intargc,char*argv){pthreadtid,idpthreadtmonthidintretendtime=time()*初始化信号量为*ret=seminit(sem,,)if(ret!=){perror("seminit")}*创建两个线程*ret=pthreadcreate(id,,(void*)pthread,)if(ret!=)perror("pthreadcread")ret=pthreadcreate(id,,(void*)pthread,)if(ret!=)perror("pthreadcread")pthreadjoin(id,)pthreadjoin(id,)exit()}voidpthread(void*arg){intiwhile(time()<endtime){*信号量减一P操作*semwait(sem)for(i=i<i){sleep()lockvarprintf("lockvar=dn",lockvar)}printf("pthread:lockvar=dn",lockvar)*信号量加一V操作*sempost(sem)sleep()}}voidpthread(void*arg){intnolock=intretwhile(time()<endtime){*信号量减一P操作*semwait(sem)printf("pthread:pthreadgotlocklockvar=dn",lockvar)*信号量加一V操作*sempost(sem)sleep()}}程序运行结果如下所示:root(none)tmp#semnumlockvar=lockvar=pthread:lockvar=pthread:pthreadgotlocklockvar=lockvar=lockvar=pthread:lockvar=pthread:pthreadgotlocklockvar=接下来是通过两个信号量来实现两个线程间的同步仍然完成了以上实例中对lockvar的操作。代码如下所示:*semsync*#include<stdioh>#include<stdlibh>#include<unistdh>#include<pthreadh>#include<errnoh>#include<sysipch>#include<semaphoreh>intlockvartimetendtimesemtsem,semvoidpthread(void*arg)voidpthread(void*arg)intmain(intargc,char*argv){pthreadtid,idpthreadtmonthidintretendtime=time()*初始化两个信号量一个信号量为一个信号量为*ret=seminit(sem,,)ret=seminit(sem,,)if(ret!=){perror("seminit")}*创建两个线程*ret=pthreadcreate(id,,(void*)pthread,)if(ret!=)perror("pthreadcread")ret=pthreadcreate(id,,(void*)pthread,)if(ret!=)perror("pthreadcread")pthreadjoin(id,)pthreadjoin(id,)exit()}voidpthread(void*arg){intiwhile(time()<endtime){*P操作信号量*semwait(sem)for(i=i<i){sleep()lockvarprintf("lockvar=dn",lockvar)}printf("pthread:lockvar=dn",lockvar)*V操作信号量*sempost(sem)sleep()}}voidpthread(void*arg){intnolock=intretwhile(time()<endtime){*P操作信号量*semwait(sem)printf("pthread:pthreadgotlocklockvar=dn",lockvar)*V操作信号量*sempost(sem)sleep()}}从以下结果中可以看出该程序确实实现了先运行线程二再运行线程一。root(none)tmp#semnumpthread:pthreadgotlocklockvar=lockvar=lockvar=pthread:lockvar=pthread:pthreadgotlocklockvar=lockvar=lockvar=pthread:lockvar=实验内容“生产者消费者”实验.实验目的“生产者消费者”问题是一个著名的同时性编程问题的集合。通过编写经典的“生产者消费者”问题的实验读者可以进一步熟悉Linux中多线程编程并且掌握用信号量处理线程间的同步互斥问题。.实验内容“生产者消费者”问题描述如下。有一个有限缓冲区和两个线程:生产者和消费者。他们分别把产品放入缓冲区和从缓冲区中拿走产品。当一个生产者在缓冲区满时必须等待当一个消费者在缓冲区空时页必须等待。它们之间的关系如下图所示:图生产者消费者问题描述这里要求用有名管道来模拟有限缓冲区用信号量来解决生产者消费者问题中的同步和互斥问题。.实验步骤()信号量的考虑这里使用个信号量其中两个信号量avail和full分别用于解决生产者和消费者线程之间的同步问题mutex是用于这两个线程之间的互斥问题。其中avail初始化为N(有界缓冲区的空单元数)mutex初始化为full初始化为。()画出流程图本实验流程图如下图所示。图“生产者消费者”实验流程图()编写代码本实验代码如下:*productc*#include<stdioh>#include<stdlibh>#include<unistdh>#include<pthreadh>#include<errnoh>#include<sysipch>#include<semaphoreh>#include<fcntlh>#defineFIFO"myfifo"#defineNintlockvartimetendtimecharbufrsemtmutex,full,availintfdvoidpthread(void*arg)voidpthread(void*arg)intmain(intargc,char*argv){pthreadtid,idpthreadtmonthidintretendtime=time()*创建有名管道*if((mkfifo(FIFO,OCREAT|OEXCL)<)(errno!=EEXIST))printf("cannotcreatefifoservern")printf("Preparingforreadingbytesn")memset(bufr,,sizeof(bufr))*打开管道*fd=open(FIFO,ORDWR|ONONBLOCK,)if(fd==){perror("open")exit()}*初始化互斥信号量为*ret=seminit(mutex,,)*初始化avail信号量为N*ret=seminit(avail,,N)*初始化full信号量为*ret=seminit(full,,)if(ret!=){perror("seminit")}*创建两个线程*ret=pthreadcreate(id,,(void*)productor,)if(ret!=)perror("pthreadcread")ret=pthreadcreate(id,,(void*)consumer,)if(ret!=)perror("pthreadcread")pthreadjoin(id,)pthreadjoin(id,)exit()}*生产者线程*voidproductor(void*arg){inti,nwritewhile(time()<endtime){*P操作信号量avail和mutex*semwait(avail)semwait(mutex)*生产者写入数据*if((nwrite=write(fd,"hello",))==){if(errno==EAGAIN)printf("TheFIFOhasnotbeenreadyetPleasetrylatern")}elseprintf("writehellototheFIFOn")*V操作信号量full和mutex*sempost(full)sempost(mutex)sleep()}}*消费者线程*voidconsumer(void*arg){intnolock=intret,nreadwhile(time()<endtime){*P操作信号量full和mutex*semwait(full)semwait(mutex)memset(bufr,,sizeof(bufr))if((nread=read(fd,bufr,))==){if(errno==EAGAIN)printf("nodatayetn")}printf("readsfromFIFOn",bufr)*V操作信号量avail和mutex*sempost(avail)sempost(mutex)sleep()}}.实验结果运行该程序得到如下结果:root(none)tmp#execPreparingforreadingbyteswritehellototheFIFOreadhellofromFIFOwritehellototheFIFOreadhellofromFIFOwritehellototheFIFOreadhellofromFIFOwritehellototheFIFOreadhellofromFIFO本章小结本章首先介绍了线程的基本概念、线程的分类和线程的发展历史可以看出线程技术已有了很大的进展。接下来讲解了Linux中线程基本操作的API函数包括线程的创建及退出修改线程属性的操作对每种操作都给出了简短的实例并加以说明。再接下来本章讲解了线程的控制操作由于线程的操作必须包括线程间的同步互斥操作包括互斥锁线程控制和信号量线程控制。最后本章的实验是一个经典的生产者消费者问题可以使用线程机制很好地实现希望读者能够认真地编程实验进一步理解多线程的同步、互斥操作。思考与练习通过查找资料查看主流的嵌入式操作系统(如嵌入式LinuxVxworks等)是如何处理多线程操作的?doc用户地址空间线程三线程二线程一进程doc消费者生产者N…vsd��开始�初始化信号量为�P操作�线程一执行�V操作�P操作�线程二执行�V操作�结束�vsd��开始�建立有名管道�打开有名管道�初始化三个信号量创建消费者和生产者两个线程P操作(avail)P操作(mutex)读管道V操作(full)V操作(mutex)P操作(full)P操作(mutex)写管道V操作(avail)V操作(mutex)结束生产者线程消费者线程

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

评分:

/25

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利