首页 6_Linux线程并发控制.pdf

6_Linux线程并发控制.pdf

6_Linux线程并发控制.pdf

上传者: tanweinan 2012-01-15 评分1 评论0 下载29 收藏0 阅读量198 暂无简介 简介 举报

简介:本文档为《6_Linux线程并发控制pdf》,可适用于专题技术领域,主题内容包含线程并发访问线程并发访问AndrewHuang课程内容:l可重入函数和线程安全函数lLinux线程锁lLinux线程同步l符等。

线程并发访问线程并发访问AndrewHuang<bluedrumcom>课程内容:l可重入函数和线程安全函数lLinux线程锁lLinux线程同步lWindows多线程编程可重入函数和线程安全函数可重入函数和线程安全函数l单线程程序只有一个控制流。不需要考虑一个资源(比如静态或全局变量如何处理)被同时访问或并发访问,但是多线程程序必须考虑并发访问一个资源。为了保证资源的完整性为多线程程序写的代码必须是可重入的和线程安全的。–线程安全的(ThreadSafe):如果一个函数在同一时刻可以被多个线程安全地调用就称该函数是线程安全的。线程安全函数解决多个线程调用函数时访问共享资源的冲突问题。–可重入(Reentrant):函数可以由多于一个线程并发使用而不必担心数据错误。可重入函数可以在任意时刻被中断稍后再继续运行不会丢失数据。可重入性解决函数运行结果的确定性和可重复性。可重入函数和线程安全关系l两者之间的关系:–、一个函数对于多个线程是可重入的则这个函数是线程安全的。–、一个函数是线程安全的但并不一定是可重入的。–、可重入性要强于线程安全性。l两者的侧重点不一样,不安全的原因主要在于使用全局共享的资源–静态变量,全局变量可重入函数l可重入的函数需要做到如下三点–不在函数内部使用静态或全局数据–不返回静态或全局数据所有数据都由函数的调用者提供。–使用本地数据或者通过制作全局数据的本地拷贝来保护全局数据。–如果必须访问全局变量利用加锁机制(如互斥量)来保护全局变量。–不调用不可重入函数PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问函数可重入化lC标准库中很多函数是不可重入的因为在函数内部使用静态数据–strtok()是非可重入的因为它在内部存储了被标记分割的字符串ctime()函数也是非可重入的它返回一个指向静态数据的指针而该静态数据在每次调用中都被覆盖重写。–为了解决可重入问题,很多C库提供可以重入版本,如strtokr()就是strtok的可重入版本线程函数编写l在C语言中局部变量是在栈上分配的。因此任何未使用静态数据或其他共享资源的函数都是线程安全的l以下是线程安全,多线程并发控制l多线程编程的主要问题–是对共享数据的保护即在多个线程同时访问同一个数据时,保证数据读写安全–线程安全函数除了尽量不使用静态或全局变量,另一个主要手段是加锁–全局共享的数据一般建议使用volatile关键字保护lvolatileintalpthread线程一般通三种机制来完成并发控制–线程互斥锁(pthreadmutex)–线程条件变量(pthreadcond)char*strtoupper(char*string){不可重入版本staticcharbufferMAXSTRINGSIZEintindexfor(index=stringindexindex)bufferindex=toupper(stringindex)bufferindex=returnbuffer}intdiff(intx,inty){intdeltadelta=yxif(delta<)delta=deltareturndelta}PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问–Posix匿名信号量pthread互斥锁线程互斥锁l一种在多线程程序中同步访问手段是使用互斥量。程序员给某个对象加上一把“锁”每次只允许一个线程去访问它。如果想对代码关键部分的访问进行控制你必须在进入这段代码之前锁定一把互斥量在完成操作之后再打开它。l可以通过使用pthread的互斥接口保护数据确保同一时间只有一个线程访问数据。互斥量从本质上说是一把锁在访问共享资源前对互斥量进行加锁在访问完成后释放互斥量上的锁。对互斥量进行加锁以后任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞所以在该互斥锁上的阻塞线程都会变成可进行状态第一个变成运行状态的线程可以对互斥量加锁其他线程在次被阻塞等待下次运行状态。lmutex即为互斥的含义l只在同一个进程内的线程之间有效,无法跨进程使用l相当于资源数只为的信号量l操作非常类似信号量操作,因此线程锁能做的事,可以用等效的信号量代码来操作l线程锁机制同时也不是异步信号安全的也就是说不应该在信号处理过程中使用互斥锁否则容易造成死锁。线程互斥锁的数据结构l互斥量用pthreadmutext数据类型来表示在使用互斥量以前必须首先对它进行初始化–可以把它置为常量PTHREADMUTEXINITIALIZER(只对静态分配的互斥量)–也可以通过调用pthreadmutexinit函数进行初始化如果动态地分配互斥量那么释放内存前需要调用pthreadmutexdestroy线程互斥锁使用l初始化一个互斥锁–pthreadmutexinit()等同于seminit()l互斥锁加锁–pthreadmutexlock(),等同于semwait()l互斥锁测试–pthreadmutextrylock(),等于于semtrywait()l互斥锁解锁–pthreadmutexunlock(),等同于sempost()l互斥锁销毁–pthreadmutexdestory,等同于semdestroy()初始化锁PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问名称::pthreadmutexinit功能:初始化互斥锁。头文件:#include<pthreadh>函数原形:intpthreadmutexinit(pthreadmutext*mutex,constpthreadmutext*attr)参数:mutex互斥量attr互斥锁属性返回值:若成功则返回否则返回错误编号。销毁锁名称::pthreadmutexdestroy功能:释放对互斥变量分配的资源头文件:#include<pthreadh>函数原形:intpthreadmutexdestroy(pthreadmutext*mutex)参数:返回值:若成功则返回否则返回错误编号。上锁解锁名称::pthreadmutexlockpthreadmutextrylockpthreadmutexunlock功能:对互斥量加减锁头文件:#include<pthreadh>函数原形:intpthreadmutexlock(pthreadmutext*mutex)intpthreadmutextrylock(pthreadmutext*mutex)intpthreadmutexunlock(pthreadmutext*mutex)参数:返回值:若成功则返回否则返回错误编号。实例charbufferintbufferhasitem=pthreadmutextmutexstructtimespecdelayPDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问*接上一页*intmain(void){pthreadtreader*创建一个全局互斥量,表示默认属性,用于保护buffer*pthreadmutexinit(mutex,)pthreadcreate(reader,,(void*)readerfunction,)writerfunction()return}voidwriterfunction(void){inti,cnt=while(){pthreadmutexlock(mutex)if(bufferhasitem==){strcpy(buffer,"hellod")printf("write(d)sn",cnt,buffer)bufferhasitem=cnt*sleep()*}pthreadmutexunlock(mutex)*线程延时一段时间**pthreaddelaynp(delay)*for(i=i<i)}}voidreaderfunction(void){inti,cnt=while(){pthreadmutexlock(mutex)if(bufferhasitem==){printf("read(d)sn",cnt,buffer)bufferhasitem=cnt}*解锁*pthreadmutexunlock(mutex)*pthreaddelaynp(delay)*for(i=i<i)}}PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问互斥锁l互斥量需要时间来加锁和解锁。锁住较少互斥量的程序通常运行得更快。所以互斥量应该尽量少够用即可每个互斥量保护的区域应则尽量小。l互斥量的本质是串行执行。如果很多线程需要领繁地加锁同一个互斥量则线程的大部分时间就会在等待这对性能是有害的。如果互斥量保护的数据(或代码)包含彼此无关的片段则可以特大的互斥量分解为几个小的互斥量来提高性能。这样任意时刻需要小互斥量的线程减少线程等待时间就会减少。所以互斥量应该足够多(到有意义的地步)每个互斥量保护的区域则应尽量的少。posix匿名信号量posix匿名信号量lposix命名信号量。这些信号量由一个name参数标识它通常指代文件系统中的某个文件。然而posix也提供基于内存的信号量它们由应用程序分配信号量的内存空间然后由系统初始化它们的值。l因为没有全局的名字所以只能在同一进程中使用.多用于多线程的并发控制.seminit:初始化匿名信号量名称::seminitsemdestroy功能:初始化关闭信号等头文件:#include<semaphoreh>函数原形:intseminit(semt*sem,intshared,unsignedintvalue)intsemgetvalue(semt*sem)参数:sem指向信号量的指针shared作用范围value信号量初始值返回值:若成功则返回否则返回。l基于内存的信号量是由seminit初始化的。sem参数指向必须由应用程序分配的semt变量。如果shared为那么待初始化的信号量是在同一进程的各个线程共享的否则该信号量是在进程间共享的。当shared为零时该信号量必须存放在即将使用它的所有进程都能访问的某种类型的共享内存中。跟semopen一样value参数是该信号量的初始值。l使用完一个基于内存的信号量后我们调用semdestroy关闭它。l除了semopen和semclose外其它的poisx有名信号量函数都可以用于基于内存的信号量。命名信号量与匿名信号量区别lsemopen不需要类型与shared的参数有名信号量总是可以在不同进程间共享的。lseminit不使用任何类似于OCREAT标志的东西也就是说seminit总是初始化信号量的值。因此对于一个给定的信号量我们必须小心保证只调用一次seminit。PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问lsemopen返回一个指向某个semt变量的指针该变量由函数本身分配并初始化。但seminit的第一个参数是一个指向某个semt变量的指针该变量由调用者分配然后由seminit函数初始化。lposix有名信号量是通过内核持续的一个进程创建一个信号量另外的进程可以通过该信号量的外部名(创建信号量使用的文件名)来访问它。posix基于内存的信号量的持续性却是不定的如果基于内存的信号量是由单个进程内的各个线程共享的那么该信号量就是随进程持续的当该进程终止时它也会消失。如果某个基于内存的信号量是在不同进程间同步的该信号量必须存放在共享内存区中这要只要该共享内存区存在该信号量就存在。多线程加锁#include<semaphoreh>#include<unistdh>#include<stdioh>#include<fcntlh>#include<pthreadh>#incude<stdlibh>void*threadfunction(void*arg)*线程入口函数*voidprint(void)*共享资源函数*semtbinsem*定义信号灯*intvalue*定义信号量的灯*intmain(){intn=pthreadtathreadif((seminit(binsem,,))!=)*初始化信号灯初始值为*{perror(“seminit”)exit()}while(n<)*循环创建个线程*{if((pthreadcreate(athread,,threadfunction,))!=){perror(“Threadcreationfailed”)exit()}}pthreadjoin(athread,)*等待子线程返回*}PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问pthread线程同步线程同步l同步就是线程等待某个事件的发生。只有当等待的事件发生线程才继续执行否则线程挂起并放弃处理器。当多个线程协作时相互作用的任务必须在一定的条件下同步。lLinux下的C语言多线程编程有多种线程同步机制最典型的是条件变量(conditionvariable)。l条件变量所做的事情也能被信号量所代替,但条件变量本身不是原子操作,所以任何对条件变量的操作都要用一个线程锁来保护–这也是大部代码看同步出现线程锁和线程条件变量的例子条件变量概念l与互斥锁不同条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。l条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起另一个线程使"条件成立"(给出条件成立信号)。l条件的检测是在互斥锁的保护下进行的。如果一个条件为假一个线程自动阻塞并释放等待状态改变的互斥锁。如果另一个线程改变了条件它发信号给关联的条件变量唤醒一个或多个等待它的线程重新获得互斥锁重新评价条件。如果两进程共享可读写的内存条件变量可以被用来实现这两进程间的线程同步。线程条件变量*接上一页*void*threadfunction(void*arg){semwait(binsem)*等待信号灯*print()sleep()sempost(binsem)*挂出信号灯*printf(“Ifinished,mypidisdn”,pthreadself())pthreadexit(arg)}voidprint(){printf(“Igetit,mytidisdn”,pthreadself())semgetvalue(binsem,value)*获取信号灯的值*printf(“Nowthevaluehavedn”,value)}PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问l创建条件变量–intpthreadcondinit(pthreadcondt*cond,pthreadcondattrt*condattr)l销毁条件变量–intpthreadconddestroy(pthreadcondt*cond)l等待–intpthreadcondwait(pthreadcondt*cond,pthreadmutext*mutex)l激活–pthreadcondsignal条件变量初始化名称:pthreadcondinit目标:条件变量初始化头文件:#include<pthreadh>函数原形:intpthreadcondinit(pthreadcondt*cond,constpthreadcondattrt*attr)参数:cptr条件变量attr条件变量属性返回值:成功返回出错返回错误编号。条件变量销毁名称:pthreadconddestroy目标:条件变量摧毁头文件:#include<pthreadh>函数原形:intpthreadconddestroy(pthreadcondt*cond)参数:cptr条件变量返回值:成功返回出错返回错误编号。条件变量等待名称:pthreadcondwaitpthreadcondtimedwait目标:条件变量等待头文件:#include<pthreadh>函数原形:intpthreadcondwait(pthreadcondt*cond,pthreadmutext*mutex)intpthreadcondtimedwait(pthreadcondt*cond,pthreadmutextmytex,conststructtimespec*abstime)参数:cond条件变量mutex互斥锁返回值:成功返回出错返回错误编号。l第一个参数*cond是指向一个条件变量的指针。第二个参数*mutex则是对相关的互斥PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问锁的指针。函数pthreadcondtimedwait函数类型与函数pthreadcondwait区别在于如果达到或是超过所引用的参数*abstime它将结束并返回错误ETIMEpthreadcondtimedwait函数的参数*abstime指向一个timespec结构。条件变量激活名称:pthreadcondsignalpthreadcondbroadcast目标:条件变量通知头文件:#include<pthreadh>函数原形:intpthreadcondsignal(pthreadcondt*cond)intpthreadcondbroadcast(pthreadcondt*cond)参数:cond条件变量返回值:成功返回出错返回错误编号。l参数*cond是对类型为pthreadcondt的一个条件变量的指针。当调用pthreadcondsignal时一个在相同条件变量上阻塞的线程将被解锁。如果同时有多个线程阻塞则由调度策略确定接收通知的线程。如果调用pthreadcondbroadcast,则将通知阻塞在这个条件变量上的所有线程。一旦被唤醒线程仍然会要求互斥锁。如果当前没有线程等待通知则上面两种调用实际上成为一个空操作。如果参数*cond指向非法地址则返回值EINVAL。使用框架澡堂模型l参考代码bathhousec生产者消费者模型*初始化条件变量及保护锁*pthreadmutextmutex=PTHREADMUTEXINITIALIZER*初始化互斥锁*pthreadcondtcond=PTHREADCONDINITIALIZER*初始化条件变量**等待通知*pthreadmutexlock(mutex)pthreadcondwait(cond,mutex)pthreadmutexunlock(mutex)*发送通知*pthreadmutexlock(mutex)*锁住互斥量*pthreadcondsignal(cond)*条件改变发送信号通知tb进程*pthreadmutexunlock(mutex)*解锁互斥量**销毁条件变量及保护锁*pthreadmutexdestroy(mutex)pthreadconddestroy(cond)PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问生产者-消费者模型l生产者-消费者模型是指:–.生产者进行生产将物品放入仓库同一时间只能有一个生产者将物品放入仓库如果仓库满生产者等待。–.消费者从仓库中取出物品同一时间只能有一个消费者取出物品如果仓库空消费者等待如果仓库满,则生产者等待–.生产者将物品放入仓库时消费者不能同时取–.消费者取物品时生产者不能放入物品l总之就是生产者群体或消费者群体内部是互斥的两个群体之间是同步的。程序实例l生产者消费者问题是一个著名的进程同步问题。具体描述:l有一群生产者进程在生产产品并将这些产品提供给消费者进程去消费。l为使生产者进程与消费者进程能并发执行在两者之间设置了一个具有个缓冲区的缓冲池生产者进程将它所生产的产品放入一个缓冲区中l消费者进程可从一个缓冲区中取走产品去消费。l尽管所有的生产者进程和消费者进程都是以异步方式运行的但它们之间必须保持同步即不允许消费者进程到一个空缓冲区去取产品也不允许生产者进程向一个已装满产品且尚未被取走的缓冲区中投放产品voidproducer(void){intmwhile(i<){pthreadmutexlock(mutex)bufferi=ii=iprintf("nthereareblocksinthebuffer:,,,,,,,,,n")printf("nbuffer:")for(m=m<m)printf("d",bufferm)printf("nthenumberaddedbytheproduceris:")printf("d",bufferi)printf("npointerisd",i)sempost(full)pthreadmutexunlock(mutex)}}PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问Windows线程并发控制Windows下的互斥同步机制(可选)l临界区(CriticalSection):临界区不是内核对象在用户态实现了同一进程中线程的互斥。由于使用时不需要从用户态切换到核心态所以速度很快(X系统上约为个指令周期)但其缺点是不能跨进程同步同时不能指定阻塞时的等待时间只能无限等待。–类似于线程互斥锁,速度最快l互斥体:(Mutex):互斥体实现了和临界区类似的互斥功能但区别在于:互斥体是内核对象可以实现跨进程互斥但需要在用户态和核心态之间切换速度比临界区慢得多(X系统上约为个指令周期)同时可以指定阻塞时的等待时间。–类似于Posix有名信号量l事件(Event):事件也是内核对象具有“信号态”和“无信号态”两种状态。当某一线程等待一个事件时如果事件为信号态将继续执行如果事件为无信号态那么线程被阻塞。线程能够指定阻塞时的等待时间。–类似于pthreadcondl信号量(Semaphore):信号量是一个资源计数器当某线程获取某信号量时信号量计数首先减如果计数小于那么该线程被阻塞当某县城释放某信号量时信号量*接上一页*voidconsumer(void){intmwhile(j<){intxpthreadmutexlock(mutex)semwait(full)if(bufferj==)breakx=bufferjbufferj=j=jprintf("nbuffer:")for(m=m<m)printf("d",bufferm)printf("ndisremovedformthebufferbyconsumer",x)printf("nThepresentpointerisd",j)pthreadmutexunlock(mutex)sempost(empty)}}PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn线程并发访问计数首先加如果计数小于或等与那么唤醒某被阻塞的线程并执行之。对信号量的总结如下:–.如果计数器m大于表示还有m个资源可以访问此时信号量线程等待队列中没有线程被阻塞新的线程访问资源也不会被阻塞–.如果计数器m等与表示没有资源可以访问此时信号量线程等待队列中没有线程被阻塞但新的线程访问资源会被阻塞–.如果计数器m小于表示没有资源可以访问此时信号量线程等待队列中有abs(m)个线程被阻塞新的线程访问资源会被阻塞l信号量常被用于保证对多个资源进行同步访问。互斥量的操作l互斥量类型HANDLE–等同pthreadmutextl创建一个互斥量CreateMutex–pthreadmutexinitl关闭销毁一个互斥量CloseHandle–pthreaddestroyl加锁一个互斥量WaitForSingleObject–pthreadmutexlockl解锁一个互斥量ReleaseMutex–pthreadmutexunlock思考题l同样程序你可以用多线程和多进程模型来实现,你是如何权衡采用哪一种体系结构的l在程序设计中,对公共资源(比如缓冲区等)的操作和访问经常需要使用锁来进行保护,但在大并发系统中过多的锁会导致效率很低,通常有那些方法可以尽量避免或减少锁的使用课堂练习l把自已写的链表库改成支持多线程的版本l把澡堂模型的同步机制由条件变量改成用信号量来控制l将testmutex例子用Windows来实现–线程用beginThread或CreateThread–加锁用WaitSingleObject来lock,用ReleaseMutex来解锁–具体对应表参见上页,调用请参见MSDN–最好定义一组相同接口的宏来封装操作系统细节,PDF文件使用"pdfFactory"试用版本创建wwwfineprintcn

职业精品

分销渠道选择.ppt

辞职申请书(优质范文).doc

公司年检申请书doc.doc

厂家和经销商代理合同.doc

用户评论

0/200
    暂无评论
上传我的资料

精彩专题

相关资料换一换

资料评价:

/ 13
所需积分:1 立即下载

意见
反馈

返回
顶部