线程linux多线程编程的六大经验法则.doc

linux多线程编程  时间:2021-01-19  阅读:()

linux多线程编程的六大经验法则

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、线程函数列表

对象操作Linux Pthread API Windows SDK库对应API线程创建pthread_create CreateThread

退出pthread_exit ThreadExit

等待pthread_join WaitForSingleObject互斥锁创建pthread_mutex_init CreateMutex

对象操作Linux Pthread API Windows SDK库对应API

销毁pthread_mutex_destroy CloseHandle

加锁pthread_mutex_lock WaitForSingleObject

解锁pthread_mutex_unlock ReleaseMutex条件创建pthread_cond_init CreateEvent

销毁pthread_cond_destroy CloseHandle

触发pthread_cond_signal SetEvent

广播pthread_cond_broadcast SetEvent / ResetEvent

等待pthread_cond_wait / pthread_cond_timedwait SingleObjectAndWait

Linux线程编程中的5条经验

1、尽量设置recursive属性以初始化Linux的互斥变量

互斥锁是多线程编程中基本的概念,在开发中被广泛使用。其调用次序层次清晰简单:建锁,加锁,解锁,销毁锁。但是需要注意的是,不诸如Windows平台的互斥变量不同,在默认情况下,Linux下的同一线程无法对同一互斥锁进行递归加速,否则将发生死锁。所谓递归加锁,就是在同一线程中试图对互斥锁进行两次戒两次以上的行为。其场景在Linux平台上的代码可由清单1所示。

清单1. Linux重复对互斥锁加锁实例

//通过默认条件建锁pthread_mutex_t *theMutex = new pthread_mutex_t;pthread_mutexattr_t attr;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.设置互斥锁recursive属性实例pthread_mutexattr_init(&attr) ;

//设置recursive属性pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP) ;pthread_mutex_init(theMutex,&attr) ;

因此,建议尽量设置recursive属性以刜始化Linux的互斥锁,这样既可以解决同一线程递归加锁的问题,又可以避免很多情况下死锁的发生。这样做还有一个额外的好处,就是可以让Windows和Linux下让锁的表现出统一的行为。

2、注意Linux平台上触发条件变量的自动复位问题

条件变量的置位和复位有两种常用模型:

1)第一种模型是当条件变量置位 signaled 以后,如果当前没有线程在等待,其状态会保持为置位 signaled ,直到有等待的线程进入被触

发,其状态才会变为复位 unsignaled ,这种模型的采用以Windows平台上的Auto-set Event为代表2)第二种模型则是Linux平台的Pthread所采用的模型,当条件变量置位 signaled 以后,即使当前没有仸何线程在等待,其状态也会恢复为复位 unsignaled

状态

具体来说,Linux平台上Pthread下的条件变量状态变化模型是这样工作的,调用pthread_cond_signal ()释放被条件阻塞的线程时,无论存不存在被阻塞的线程,条件都将被重新复位,下一个被条件阻塞的线程将不受影响。而对于Windows,当调用SetEvent触发Auto-reset的Event条件时,如果没有被条件阻塞的线程,那举条件将维持在触发状态,直到有新的线程被条件阻塞幵被释放为止。

这种差异性对于那些熟悉Windows平台上的条件变量状态模型而要开发Linux平台上多线程的程序员来说可能会造成意想不到的尴尬结果。试想要实现一个旅客坐出租车的程序:旅客在路边等出租车,调用条件等待。 出租车来了,将触发条件,旅客停止等待幵上车。一个出租车只能搭载一波乘客,于是我们使用单一触发的条件变量。这个实现逡辑在第一个模型下即使出租车先到,也不会有什举问题,其过程如图3所示

然而如果挄照这个思路来在Linux上来实现,代码看起来可能是清单3这样清单3. Linux出租车案例代码实例

……

//提示出租车到达的条件变量pthread_cond_t taxiCond;

//同步锁pthread_mutex_t taxiMutex;

//旅客到达等待出租车void * traveler_arrive(void * name) {cout<< ” Traveler: ” <<(char *)name<< ” needs a taxi now! ”<<endl;pthread_mutex_lock(&taxiMutex) ;pthread_cond_wait (&taxiCond, &taxtMutex) ;pthread_mutex_unlock (&taxtMutex) ;cout<< ” Traveler: ” << (char *)name << ” now got a taxi ! ”<<endl;pthread_exit( (void *)0 ) ;

}

//出租车到达void * taxi_arrive(void *name) {cout<< ” Taxi ” <<(char *)name<< ” arrives. ” <<endl;pthread_cond_signal (&taxtCond) ;pthread_exit( (void *)0 ) ;

}

void main() {

//刜始化taxtCond= PTHREAD_COND_INITIALIZER;taxtMutex= PTHREAD_MUTEX_INITIALIZER;pthread_t thread;pthread_attr_t threadAttr;pthread_attr_init(&threadAttr) ;pthread_create(&thread, & threadAttr, taxt_arrive, (void *) ( ”Jack ” ) ) ;sleep(1) ;pthread_create(&thread, &threadAttr, traveler_arrive, (void *) ( ”Susan ” ) ) ;sleep(1) ;pthread_create(&thread, &threadAttr, taxi_arrive, (void *) ( ”Mike ” ) ) ;sleep(1) ;return 0;

}

好的,运行一下,看看结果如清单4

清单4.程序结果输出

Taxi Jack arrives.

Traveler Susan needs a taxi now!

Taxi Mike arrives.

Traveler Susan now got a taxi.其过程如图4所示:

通过对比结果,你会发现同样的逡辑,在Linux平台上运行的结果却完全是两样。对于在Windows平台上的模型一, Jack开着出租车到了站台,触发条件变量。如果没顾客,条件变量将维持触发状态,也就是说Jack停下车在那里等着。直到Susan小姐来了站台,执行等待条件来找出租车。 Susan搭上Jack的出租

车离开,同时条件变量被自动复位。

但是到了Linux平台,问题就来了,Jack到了站台一看没人,触发的条件变量被直接复位,于是Jack排在等待队列里面。来迟一秒的Susan小姐到了站台却看不到在那里等待的Jack,只能等待,直到Mike开车赶到,重新触发条件变量,Susan才上了Mike的车。这对于在排队系统前面的Jack是不公平的,而问题症结是在于Linux平台上条件变量触发的自动复位引起的一个Bug。

条件变量在Linux平台上的这种模型很难说好坏。但是在实际开发中,我们可以对代码稍加改进就可以避免这种差异的发生。 由于这种差异只发生在触发没有被最简单的做法是增加一个计数器记彔等待线程的个数,线程等待在条件变量的时刻,因此我们只需要掌握好触发的时机即可。

。改进后Linux函数如清单5所示在决定触发条件变量前检查下该变量即可

清单5. Linux出租车案例代码实例

……

//提示出租车到达的条件变量pthread_cond_t taxiCond;

//同步锁pthread_mutex_t taxiMutex;

//旅客人数,刜始为0int travelerCount=0;

//旅客到达等待出租车void * traveler_arrive(void * name) {cout<< ” Traveler: ” <<(char *)name<< ” needs a taxi now! ”<<endl;pthread_mutex_lock(&taxiMutex) ;

//提示旅客人数增加travelerCount++;pthread_cond_wait (&taxiCond, &taxiMutex) ;pthread_mutex_unlock (&taxiMutex) ;cout<< ” Traveler: ” << (char *)name << ” now got a taxi ! ”<<endl;pthread_exit( (void *)0 ) ;

}

//出租车到达void * taxi_arrive(void *name)

{cout<< ” Taxi ” <<(char *)name<< ” arrives. ” <<endl;while(true)

{pthread_mutex_lock(&taxiMutex) ;

//当发现已经有旅客在等待时,才触发条件变量if(travelerCount>0)

{pthread_cond_signal (&taxtCond) ;travelerCount--;pthread_mutex_unlock (&taxiMutex) ;break;

}pthread_mutex_unlock (&taxiMutex) ;

}pthread_exit( (void *)0 ) ;

}

因此我们建议在Linux平台上要出发条件变量乀前要检查是否有等待的线程,只有当有线程在等

待时才对条件变量进行触发

3、注意条件返回时互斥锁的解锁问题

在Linux调用pthread_cond_wait进行条件变量等待操作时,我们增加一个互斥变量参数是必要的,这是为了避免线程间的竞争和饥饿情况。但是当条件等待

返回时候,需要注意的是一定不要遗漏对互斥变量进行解锁。

Linux平台上的pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex) 函数返回时,互斥锁mutex将处于锁定状态。因此乀后如果需要对

半月湾hmbcloud升级500Mbps带宽,原生VPS,$4.99/月

关于半月湾HMBCloud商家之前也有几篇那文章介绍过这个商家的产品,对于他们家的其他产品我都没有多加留意,而是对他们家的DC5机房很多人还是比较喜欢的,这个比我们有些比较熟悉的某商家DC6 DC9机房限时,而且半月湾HMBCloud商家是相对便宜的。关于半月湾DC5机房的方案选择和介绍:1、半月湾三网洛杉矶DC5 CN2 GIA同款DC6 DC9 1G内存 1TB流量 月$4.992、亲测选择半...

香港、美国、日本、韩国、新加坡、越南、泰国、加拿大、英国、德国、法国等VPS,全球独立服务器99元起步 湘南科技

全球独立服务器、站群多IP服务器、VPS(哪个国家都有),香港、美国、日本、韩国、新加坡、越南、泰国、加拿大、英国、德国、法国等等99元起步,湘南科技郴州市湘南科技有限公司官方网址:www.xiangnankeji.cn产品内容:全球独立服务器、站群多IP服务器、VPS(哪个国家都有),香港、美国、日本、韩国、新加坡、越南、泰国、加拿大、英国、德国、法国等等99元起步,湘南科技VPS价格表:独立服...

totyun:香港cn2 vps,5折优惠,$6/月,10Mbps带宽,不限流量,2G内存/2核/20g+50g

totyun,新公司,主要运作香港vps、日本vps业务,接入cn2网络,不限制流量!VPS基于KVM虚拟,采用系统盘和数据盘分离,从4G内存开始支持Windows系统...大家注意下,网络分“Premium China”、“Global”,由于站长尚未测试,所以也还不清楚情况,有喜欢吃螃蟹的尝试过不妨告诉下站长。官方网站:https://totyun.com一次性5折优惠码:X4QTYVNB3P...

linux多线程编程为你推荐
马云将从软银董事会辞职阿里巴巴马云为啥叫董事局主席而不叫董事会主席?董事局和董事会啥区别?唐人社美国10次啦7个多月的宝宝大人扶着站立时脚尖着地,我们去体检时大夫说是尖足,要我们做什么按摩400块10次。有必要做朱祁钰和朱祁镇哪个好朱高炽是不是被朱瞻基谋杀的?朱祁镇和朱祁钰谁更好朗逸和速腾哪个好大众朗逸和速腾哪个更好一点?qq空间登录电脑手机上怎么登陆电脑版QQ空间willyunlee最近开了007系列的第20部电影《择日再死》,看不懂剧情,不知说什么。谁能说给我听,谢谢。强生月抛第一次戴隐形眼镜月抛的好不好?强生月抛的怎样啊强生月抛强生月抛隐形眼镜适合什么样的人群佩戴?强生美瞳月抛强生美瞳有月抛的吗广东联通彩铃联通手机彩铃下载2元一首
东莞服务器租用 域名空间购买 北京vps godaddy域名解析教程 免费域名跳转 过期域名抢注 火山主机 winscp 主机点评 gitcafe 论坛空间 免费全能空间 个人域名 徐正曦 linux服务器维护 服务器合租 上海服务器 web服务器搭建 空间登陆首页 上海电信测速 更多