linux 的多线程编程的高效开发经验

上传人:第*** 文档编号:32687942 上传时间:2018-02-12 格式:DOCX 页数:12 大小:49.14KB
返回 下载 相关 举报
linux 的多线程编程的高效开发经验_第1页
第1页 / 共12页
linux 的多线程编程的高效开发经验_第2页
第2页 / 共12页
linux 的多线程编程的高效开发经验_第3页
第3页 / 共12页
linux 的多线程编程的高效开发经验_第4页
第4页 / 共12页
linux 的多线程编程的高效开发经验_第5页
第5页 / 共12页
点击查看更多>>
资源描述

《linux 的多线程编程的高效开发经验》由会员分享,可在线阅读,更多相关《linux 的多线程编程的高效开发经验(12页珍藏版)》请在金锄头文库上搜索。

1、背景Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多线程 API 有一些细微和隐晦的差别。不注意这些 Linux 上的一些开发陷阱,常常会导致程序问题不穷,死锁不断。本文中我们从 5 个方面总结出 Linux 多线程编程上的问题,并分别引出相关改善的开发经验,用以避免这些的陷阱。我们希望这些经验可以帮助读者们能更好更快的熟悉 Linux 平台的多线程编程。我们假设读者都已经很熟悉 Linux 平台上基本的线程编程的 Pthread 库 API 。其他的第三方用以线程编程的库,如 boost,将不会在本文中提及。本文中主要涉及的题材包括线程开发中的线程管理,互斥变量,

2、条件变量等。进程概念将不会在本文中涉及。回页首Linux 上线程开发 API 的概要介绍多线程开发在 Linux 平台上已经有成熟的 Pthread 库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。条件操作有 5 种操作:创建,销毁,触发,广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。线程,互斥锁,条件在 Linux 平台上对应的 API 可以用表 1 归纳。为了方便熟悉 Windows 线程编程的读者熟悉 Linu

3、x 多线程开发的 API,我们在表中同时也列出 Windows SDK 库中所对应的 API 名称。表 1. 线程函数列表对象 操作Linux Pthread API Windows SDK 库对应 API创建pthread_create CreateThread退出pthread_exit ThreadExit线程等待pthread_join WaitForSingleObject创建pthread_mutex_init CreateMutex销毁pthread_mutex_destroy CloseHandle互斥锁加 pthread_mutex_lock WaitForSingleObj

4、ect锁解锁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_timedwaitSingleObjectAndWait多线程开发在 Linux 平台上已经有成熟的 Pthread 库支持。其涉及的多线程开发的最基本概念主要包含三

5、点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。条件操作有 5 种操作:创建,销毁,触发,广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。回页首Linux 线程编程中的 5 条经验尽量设置 recursive 属性以初始化 Linux 的互斥变量互斥锁是多线程编程中基本的概念,在开发中被广泛使用。其调用次序层次清晰简单:建锁,加锁,解锁,销毁锁。但是需要注意的是,与诸如 Windows 平台的互斥变量不同,在默认情况下,Linux 下的同一线程无法对同一互斥锁进行递

6、归加速,否则将发生死锁。所谓递归加锁,就是在同一线程中试图对互斥锁进行两次或两次以上的行为。其场景在 Linux 平台上的代码可由清单 1 所示。清单 1. Linux 重复对互斥锁加锁实例/ 通过默认条件建锁pthread_mutex_t *theMutex = new pthread_mutex_t; pthread_mutexattr_t attr; pthread_mutexattr_init( pthread_mutex_init(theMutex, / 递归加锁pthread_mutex_lock (theMutex); pthread_mutex_lock (theMutex);

7、 pthread_mutex_unlock (theMutex); pthread_mutex_unlock (theMutex);在以上代码场景中,问题将出现在第二次加锁操作。由于在默认情况下,Linux 不允许同一线程递归加锁,因此在第二次加锁操作时线程将出现死锁。Linux 互斥变量这种奇怪的行为或许对于特定的某些场景会所有用处,但是对于大多数情况下看起来更像是程序的一个 bug 。毕竟,在同一线程中对同一互斥锁进行递归加锁在尤其是二次开发中经常会需要。这个问题与互斥锁的中的默认 recursive 属性有关。解决问题的方法就是显式地在互斥变量初始化时将设置起 recursive 属性。

8、基于此,以上代码其实稍作修改就可以很好的运行,只需要在初始化锁的时候加设置一个属性。请看清单 2 。清单 2. 设置互斥锁 recursive 属性实例pthread_mutexattr_init( / 设置 recursive 属性pthread_mutexattr_settype( pthread_mutex_init(theMutex,因此,建议尽量设置 recursive 属性以初始化 Linux 的互斥锁,这样既可以解决同一线程递归加锁的问题,又可以避免很多情况下死锁的发生。这样做还有一个额外的好处,就是可以让 Windows 和 Linux 下让锁的表现统一。注意 Linux 平台

9、上触发条件变量的自动复位问题条件变量的置位和复位有两种常用模型:第一种模型是当条件变量置位(signaled)以后,如果当前没有线程在等待,其状态会保持为置位(signaled),直到有等待的线程进入被触发,其状态才会变为复位(unsignaled),这种模型的采用以 Windows 平台上的 Auto-set Event 为代表。其状态变化如图 1 所示:图 1. Windows 的条件变量状态变化流程第二种模型则是 Linux 平台的 Pthread 所采用的模型,当条件变量置位(signaled)以后,即使当前没有任何线程在等待,其状态也会恢复为复位(unsignaled)状态。其状态变

10、化如图 2 所示:图 2. Linux 的条件变量状态变化流程具体来说,Linux 平台上 Pthread 下的条件变量状态变化模型是这样工作的:调用 pthread_cond_signal() 释放被条件阻塞的线程时,无论存不存在被阻塞的线程,条件都将被重新复位,下一个被条件阻塞的线程将不受影响。而对于 Windows,当调用 SetEvent 触发 Auto-reset 的 Event 条件时,如果没有被条件阻塞的线程,那么条件将维持在触发状态,直到有新的线程被条件阻塞并被释放为止。这种差异性对于那些熟悉 Windows 平台上的条件变量状态模型而要开发 Linux 平台上多线程的程序员来

11、说可能会造成意想不到的尴尬结果。试想要实现一个旅客坐出租车的程序:旅客在路边等出租车,调用条件等待。出租车来了,将触发条件,旅客停止等待并上车。一个出租车只能搭载一波乘客,于是我们使用单一触发的条件变量。这个实现逻辑在第一个模型下即使出租车先到,也不会有什么问题,其过程如图 3 所示:图 3. 采用 Windows 条件变量模型的出租车实例流程然而如果按照这个思路来在 Linux 上来实现,代码看起来可能是清单 3 这样。清单 3. Linux 出租车案例代码实例/ 提示出租车到达的条件变量pthread_cond_t taxiCond; / 同步锁pthread_mutex_t taxiMu

12、tex; / 旅客到达等待出租车void * traveler_arrive(void * name) cout0) pthread_cond_signal( pthread_mutex_unlock ( break; pthread_mutex_unlock ( pthread_exit( (void *)0 ); 因此我们建议在 Linux 平台上要出发条件变量之前要检查是否有等待的线程,只有当有线程在等待时才对条件变量进行触发。注意条件返回时互斥锁的解锁问题在 Linux 调用 pthread_cond_wait 进行条件变量等待操作时,我们增加一个互斥变量参数是必要的,这是为了避免线程

13、间的竞争和饥饿情况。但是当条件等待返回时候,需要注意的是一定不要遗漏对互斥变量进行解锁。Linux 平台上的 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 函数返回时,互斥锁 mutex 将处于锁定状态。因此之后如果需要对临界区数据进行重新访问,则没有必要对 mutex 就行重新加锁。但是,随之而来的问题是,每次条件等待以后需要加入一步手动的解锁操作。正如前文中乘客等待出租车的 Linux 代码如清单 6 所示:清单 6. 条件变量返回后的解锁实例void * traveler_arrive(void * nam

14、e) couttv_nsec = now.tv_usec * 1000 + (dwMilliseconds % 1000) * 1000000; abstime -tv_sec = now.tv_sec + dwMilliseconds / 1000;Linux 的绝对时间看似简单明了,却是开发中一个非常隐晦的陷阱。而且一旦你忘了时间转换,可以想象,等待你的错误将是多么的令人头疼:如果忘了把相对时间转换成绝对时间,相当于你告诉系统你所等待的超时时间是过去式的 1970 年 1 月 1 号某个时间段,于是操作系统毫不犹豫马上送给你一个 timeout 的返回值,然后你会举着拳头抱怨为什么另外一个

15、同步线程耗时居然如此之久,并一头扎进寻找耗时原因的深渊里。正确处理 Linux 平台下的线程结束问题在 Linux 平台下,当处理线程结束时需要注意的一个问题就是如何让一个线程善始善终,让其所占资源得到正确释放。在 Linux 平台默认情况下,虽然各个线程之间是相互独立的,一个线程的终止不会去通知或影响其他的线程。但是已经终止的线程的资源并不会随着线程的终止而得到释放,我们需要调用 pthread_join() 来获得另一个线程的终止状态并且释放该线程所占的资源。 Pthread_join() 函数的定义如清单 9 。清单 9. pthread_join 函数定义int pthread_joi

16、n(pthread_t th, 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 函数定义int pthread_detach(pthread_

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 中学教育 > 职业教育

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号