Linux 下多线程编程.pdf

Linux 下多线程编程.pdf

ID:52242193

大小:245.27 KB

页数:12页

时间:2020-03-25

上传者:asd881529
Linux 下多线程编程.pdf_第1页
Linux 下多线程编程.pdf_第2页
Linux 下多线程编程.pdf_第3页
Linux 下多线程编程.pdf_第4页
Linux 下多线程编程.pdf_第5页
资源描述:

《Linux 下多线程编程.pdf》由会员上传分享,免费在线阅读,更多相关内容在行业资料-天天文库

本文中我们针对Linux上多线程编程的主要特性总结出5条经验,用以改善Linux多线程编程的习惯和避免其中的开发陷阱。在本文中,我们穿插一些Windows的编程用例用以对比Linux特性,以加深读者印象。背景Linux平台上的多线程程序开发相对应其他平台(比如Windows)的多线程API有一些细微和隐晦的差别。不注意这些Linux上的一些开发陷阱,常常会导致程序问题不穷,死锁不断。本文中我们从5个方面总结出Linux多线程编程上的问题,并分别引出相关改善的开发经验,用以避免这些的陷阱。我们希望这些经验可以帮助读者们能更好更快的熟悉Linux平台的多线程编程。我们假设读者都已经很熟悉Linux平台上基本的线程编程的Pthread库API。其他的第三方用以线程编程的库,如boost,将不会在本文中提及。本文中主要涉及的题材包括线程开发中的线程管理,互斥变量,条件变量等。进程概念将不会在本文中涉及。Linux上线程开发API的概要介绍多线程开发在Linux平台上已经有成熟的Pthread库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待3种。互斥锁则包括4种操作,分别是创建,销毁,加锁和解锁。条件操作有5种操作:创建,销毁,触发,广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。线程,互斥锁,条件在Linux平台上对应的API可以用表1归纳。为了方便熟悉Windows线程编程的读者熟悉Linux多线程开发的API,我们在表中同时也列出WindowsSDK库中所对应的API名称。表1.线程函数列表对象操LinuxPthreadAPIWindowsSDK库对应作API线程创pthread_createCreateThread建退pthread_exitThreadExit出等pthread_joinWaitForSingleObject待互斥创pthread_mutex_initCreateMutex锁建销pthread_mutex_destroyCloseHandle 毁加pthread_mutex_lockWaitForSingleObject锁解pthread_mutex_unlockReleaseMutex锁条件创pthread_cond_initCreateEvent建销pthread_cond_destroyCloseHandle毁触pthread_cond_signalSetEvent发广pthread_cond_broadcastSetEvent/ResetEvent播等pthread_cond_wait/SingleObjectAndWait待pthread_cond_timedwait多线程开发在Linux平台上已经有成熟的Pthread库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待3种。互斥锁则包括4种操作,分别是创建,销毁,加锁和解锁。条件操作有5种操作:创建,销毁,触发,广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。Linux线程编程中的5条经验尽量设置recursive属性以初始化Linux的互斥变量互斥锁是多线程编程中基本的概念,在开发中被广泛使用。其调用次序层次清晰简单:建锁,加锁,解锁,销毁锁。但是需要注意的是,与诸如Windows平台的互斥变量不同,在默认情况下,Linux下的同一线程无法对同一互斥锁进行递归加速,否则将发生死锁。所谓递归加锁,就是在同一线程中试图对互斥锁进行两次或两次以上的行为。其场景在Linux平台上的代码可由清单1所示。清单1.Linux重复对互斥锁加锁实例//通过默认条件建锁pthread_mutex_t*theMutex=newpthread_mutex_t;pthread_mutexattr_tattr; pthread_mutexattr_init(&attr);pthread_mutex_init(theMutex,&attr);pthread_mutexattr_destroy(&attr);//递归加锁pthread_mutex_lock(theMutex);pthread_mutex_lock(theMutex);pthread_mutex_unlock(theMutex);pthread_mutex_unlock(theMutex);在以上代码场景中,问题将出现在第二次加锁操作。由于在默认情况下,Linux不允许同一线程递归加锁,因此在第二次加锁操作时线程将出现死锁。Linux互斥变量这种奇怪的行为或许对于特定的某些场景会所有用处,但是对于大多数情况下看起来更像是程序的一个bug。毕竟,在同一线程中对同一互斥锁进行递归加锁在尤其是二次开发中经常会需要。这个问题与互斥锁的中的默认recursive属性有关。解决问题的方法就是显式地在互斥变量初始化时将设置起recursive属性。基于此,以上代码其实稍作修改就可以很好的运行,只需要在初始化锁的时候加设置一个属性。请看清单2。清单2.设置互斥锁recursive属性实例pthread_mutexattr_init(&attr);//设置recursive属性pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);pthread_mutex_init(theMutex,&attr);因此,建议尽量设置recursive属性以初始化Linux的互斥锁,这样既可以解决同一线程递归加锁的问题,又可以避免很多情况下死锁的发生。这样做还有一个额外的好处,就是可以让Windows和Linux下让锁的表现统一。注意Linux平台上触发条件变量的自动复位问题条件变量的置位和复位有两种常用模型:第一种模型是当条件变量置位(signaled)以后,如果当前没有线程在等待,其状态会保持为置位(signaled),直到有等待的线程进入被触发,其状态才会变为复位(unsignaled),这种模型的采用以Windows平台上的Auto-setEvent为代表。其状态变化如图1所示: 图1.Windows的条件变量状态变化流程第二种模型则是Linux平台的Pthread所采用的模型,当条件变量置位(signaled)以后,即使当前没有任何线程在等待,其状态也会恢复为复位(unsignaled)状态。其状态变化如图2所示:图2.Linux的条件变量状态变化流程具体来说,Linux平台上Pthread下的条件变量状态变化模型是这样工作的:调用pthread_cond_signal()释放被条件阻塞的线程时,无论存不存在被阻塞的线程,条件都将被重新复位,下一个被条件阻塞的线程将不受影响。而对于Windows,当调用SetEvent触发Auto-reset的Event条件时,如果没有被条件阻塞的线程,那么条件将维持在触发状态,直到有新的线程被条件阻塞并被释放为止。这种差异性对于那些熟悉Windows平台上的条件变量状态模型而要开发Linux平台上多线程的程序员来说可能会造成意想不到的尴尬结果。试想要实现一个旅客坐出租车的程序:旅客在路边等出租车,调用条件等待。出租车来了,将触发条件,旅客停止等待并上车。一个出租车只能搭载一波乘客,于是我们使用单一触发的条件变量。这个实现逻辑在第一个模型下即使出租车先到,也不会有什么问题,其过程如图3所示: 图3.采用Windows条件变量模型的出租车实例流程然而如果按照这个思路来在Linux上来实现,代码看起来可能是清单3这样。清单3.Linux出租车案例代码实例……//提示出租车到达的条件变量pthread_cond_ttaxiCond;//同步锁pthread_mutex_ttaxiMutex;//旅客到达等待出租车void*traveler_arrive(void*name){cout<<”Traveler:”<<(char*)name<<”needsataxinow!”<0){pthread_cond_signal(&taxtCond);pthread_mutex_unlock(&taxiMutex);break;}pthread_mutex_unlock(&taxiMutex);}pthread_exit((void*)0);}因此我们建议在Linux平台上要出发条件变量之前要检查是否有等待的线程,只有当有线程在等待时才对条件变量进行触发。 注意条件返回时互斥锁的解锁问题在Linux调用pthread_cond_wait进行条件变量等待操作时,我们增加一个互斥变量参数是必要的,这是为了避免线程间的竞争和饥饿情况。但是当条件等待返回时候,需要注意的是一定不要遗漏对互斥变量进行解锁。Linux平台上的pthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t*mutex)函数返回时,互斥锁mutex将处于锁定状态。因此之后如果需要对临界区数据进行重新访问,则没有必要对mutex就行重新加锁。但是,随之而来的问题是,每次条件等待以后需要加入一步手动的解锁操作。正如前文中乘客等待出租车的Linux代码如清单6所示:清单6.条件变量返回后的解锁实例void*traveler_arrive(void*name){cout<<”Traveler:”<<(char*)name<<”needsataxinow!”<tv_nsec=now.tv_usec*1000+(dwMilliseconds%1000)*1000000;abstime->tv_sec=now.tv_sec+dwMilliseconds/1000;Linux的绝对时间看似简单明了,却是开发中一个非常隐晦的陷阱。而且一旦你忘了时间转换,可以想象,等待你的错误将是多么的令人头疼:如果忘了把相对时间转换成绝对时间,相当于你告诉系统你所等待的超时时间是过去式的1970年1月1号某个时间段,于是操作系统毫不犹豫马上送给你一个timeout的返回值,然后你会举着拳头抱怨为什么另外一个同步线程耗时居然如此之久,并一头扎进寻找耗时原因的深渊里。 正确处理Linux平台下的线程结束问题在Linux平台下,当处理线程结束时需要注意的一个问题就是如何让一个线程善始善终,让其所占资源得到正确释放。在Linux平台默认情况下,虽然各个线程之间是相互独立的,一个线程的终止不会去通知或影响其他的线程。但是已经终止的线程的资源并不会随着线程的终止而得到释放,我们需要调用pthread_join()来获得另一个线程的终止状态并且释放该线程所占的资源。Pthread_join()函数的定义如清单9。清单9.pthread_join函数定义intpthread_join(pthread_tth,void**thread_return);调用该函数的线程将挂起,等待th所表示的线程的结束。thread_return是指向线程th返回值的指针。需要注意的是th所表示的线程必须是joinable的,即处于非detached(游离)状态;并且只可以有唯一的一个线程对th调用pthread_join()。如果th处于detached状态,那么对th的pthread_join()调用将返回错误。如果你压根儿不关心一个线程的结束状态,那么也可以将一个线程设置为detached状态,从而来让操作系统在该线程结束时来回收它所占的资源。将一个线程设置为detached状态可以通过两种方式来实现。一种是调用pthread_detach()函数,可以将线程th设置为detached状态。其申明如清单10。清单10.pthread_detach函数定义intpthread_detach(pthread_tth);另一种方法是在创建线程时就将它设置为detached状态,首先初始化一个线程属性变量,然后将其设置为detached状态,最后将它作为参数传入线程创建函数pthread_create(),这样所创建出来的线程就直接处于detached状态。方法如清单11。清单11.创建detach线程代码实例………………………………….. pthread_ttid;pthread_attr_tattr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);pthread_create(&tid,&attr,THREAD_FUNCTION,arg);总之为了在使用Pthread时避免线程的资源在线程结束时不能得到正确释放,从而避免产生潜在的内存泄漏问题,在对待线程结束时,要确保该线程处于detached状态,否着就需要调用pthread_join()函数来对其进行资源回收。总结与补充本文以上部分详细介绍了Linux的多线程编程的5条高效开发经验。另外你也可以考虑尝试其他一些开源类库来进行线程开发。1.Boost库Boost库来自于由C++标准委员会类库工作组成员发起,致力于为C++开发新的类库的Boost组织。虽然该库本身并不是针对多线程而产生,但是发展至今,其已提供了比较全面的多线程编程的API支持。Boost库对于多线程支持的API风格上更类似于Linux的Pthread库,差别在于其将线程,互斥锁,条件等线程开发概念都封装成了C++类,以方便开发调用。Boost库目前对跨平台支持的很不错,不仅支持Windows和Linux,还支持各种商用的Unix版本。如果开发者想使用高稳定性的统一线程编程接口减轻跨平台开发的难度,Boost库将是首选。2.ACEACE全称是ADAPTIVECommunicationEnvironment,它是一个免费的,开源的,面向对象的工具框架,用以开发并发访问的软件。由于ACE最初是面向网络服务端的编程开发,因此对于线程开发的工具库它也能提供很全面的支持。其支持的平台也很全面,包括Windows,Linux和各种版本Unix。ACE的唯一问题是如果仅仅是用于线程编程,其似乎显得有些过于重量级。而且其较复杂的配置也让其部署对初学者而言并非易事。

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
关闭