本文们针 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,们表同时列出 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 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 已经有成熟 Pthread 库支持。其涉及线程最基本概念主包含三点:线程,互斥锁,条。其,线程操作又分线程创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别创建,销毁,加锁解锁。条操作有 5 种操作:创建,销毁,触发,广播等待。其些线程扩展概念,如信号灯等,都以通过面三个基本元素基本操作封装出。
Linux 线程编程 5 条经验
尽量设置 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 。
清单 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-set Event 代表。其状态如图 1 所示:
图 1. Windows 条变量状态流程
[img]http://dl.iteye.com/upload/attachment/232755/cf8333cb-2261-3280-822d-f817b801fdf7.gif[/img]
通过比结果,同样逻辑, 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;
// 旅客人数,始 0
int 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);
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<< ” 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 );
}
这点于熟悉 Windows 线程说尤重。 Windows SignalObjectAndWait() 函数常 Linux pthread_cond_wait() 函数被看作跨编程时等价函数。但需注意,两个函数退出时状态不样。 Windows ,SignalObjectAndWait(HANDLE a, HANDLE b, …… ) 调结束返回时状态 a b 都置位(signaled)状态,普遍使,a 经常个 Mutex 变量,这种,当返回时,Mutex a 处于解锁状态(signaled),Event b 处于置位状态(signaled), 因此,于 Mutex a 而言,们不需考虑解锁问题。而且, SignalObjectAndWait() ,如果需临界区数据进行重新访问,都需调 WaitForSingleObject() 重新加锁。这点刚好 Linux pthread_cond_wait() 完全相反。
Linux 于 Windows 这点额解锁操作区别重,定得牢记。否则从 Windows 移植 Linux 条等待操作旦忘结束解锁操作,程序肯定发生死锁。
等待绝时间问题
超时线程编程个常见概念。例如,当 Linux 使 pthread_cond_timedwait() 时就需指定超时这个参数,以便这个 API 调最只被阻塞指定时间间隔。但如果第次使这个 API 时,首先需解就这个 API 当超时参数特殊性(就如本节标题所提示那样)。们首先看这个 API 定义。 pthread_cond_timedwait() 定义看清单 7 。
清单 7. pthread_cond_timedwait() 函数定义
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
参数 abstime 这里表示超时时间相关个参数,但需注意所表示个绝时间,而不个时间间隔数值,只有当系统当时间达或超过 abstime 所表示时间时,才触发超时事。这于拥有 Windows 线程经验人说能尤困惑。因 Windows 所有 API 等待参数(如 SignalObjectAndWait,等)都相时间,
假设们指定相超时时间参数如 dwMilliseconds (单位毫秒)调超时相关函数,这样就需 dwMilliseconds 转化 Linux 绝时间参数 abstime 使。常转换如清单 8 所示:
清单 8. 相时间绝时间转换实例
/* get the current time */
struct timeval now;
gettimeofday(&now, NULL);
/* add the offset to get timeout value */
abstime ->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 函数定义
int pthread_join(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_t th);
另种创建线程时就设置 detached 状态,首先始化个线程属性变量,然其设置 detached 状态,最作参数传入线程创建函数 pthread_create(),这样所创建出线程就直接处于 detached 状态。如清单 11 。
清单 11. 创建 detach 线程代码实例
………………………………… ..
pthread_t tid;
pthread_attr_t attr;
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. ACE
ACE 全称 ADAPTIVE Communication Environment,个免费,开源,面向象工具框架,以并发访问软。由于 ACE 最面向网络服务端编程,因此于线程工具库能提供全面支持。其支持全面,包括 Windows,Linux 各种版本 Unix 。 ACE 唯问题如果仅仅于线程编程,其似显得有些过于重量级。而且其较复杂配置让其部署学而言并非易事。
背景
Linux 线程程序相应其(比如 Windows)线程 API 有些细微隐晦差别。不注意这些 Linux 些陷阱,常常致程序问题不穷,死锁不断。本文们从 5 个方面总结出 Linux 线程编程问题,并分别引出相关改善经验,以避免这些陷阱。们希望这些经验以帮助读们能更好更快熟悉 Linux 线程编程。
们假设读都已经熟悉 Linux 基本线程编程 Pthread 库 API 。其第三方以线程编程库,如 boost,不本文提及。本文主涉及题材包括线程线程管理,互斥变量,条变量等。进程概念不本文涉及。
Linux 线程 API 概介绍
线程 Linux 已经有成熟 Pthread 库支持。其涉及线程最基本概念主包含三点:线程,互斥锁,条。其,线程操作又分线程创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别创建,销毁,加锁解锁。条操作有 5 种操作:创建,销毁,触发,广播等待。其些线程扩展概念,如信号灯等,都以通过面三个基本元素基本操作封装出。
线程,互斥锁,条 Linux 应 API 以表 1 归纳。方便熟悉 Windows 线程编程读熟悉 Linux 线程 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 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 已经有成熟 Pthread 库支持。其涉及线程最基本概念主包含三点:线程,互斥锁,条。其,线程操作又分线程创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别创建,销毁,加锁解锁。条操作有 5 种操作:创建,销毁,触发,广播等待。其些线程扩展概念,如信号灯等,都以通过面三个基本元素基本操作封装出。
Linux 线程编程 5 条经验
尽量设置 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 。
清单 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-set Event 代表。其状态如图 1 所示:
图 1. Windows 条变量状态流程
[img]http://dl.iteye.com/upload/attachment/232755/cf8333cb-2261-3280-822d-f817b801fdf7.gif[/img]
通过比结果,同样逻辑, 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;
// 旅客人数,始 0
int 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);
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<< ” 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 );
}
这点于熟悉 Windows 线程说尤重。 Windows SignalObjectAndWait() 函数常 Linux pthread_cond_wait() 函数被看作跨编程时等价函数。但需注意,两个函数退出时状态不样。 Windows ,SignalObjectAndWait(HANDLE a, HANDLE b, …… ) 调结束返回时状态 a b 都置位(signaled)状态,普遍使,a 经常个 Mutex 变量,这种,当返回时,Mutex a 处于解锁状态(signaled),Event b 处于置位状态(signaled), 因此,于 Mutex a 而言,们不需考虑解锁问题。而且, SignalObjectAndWait() ,如果需临界区数据进行重新访问,都需调 WaitForSingleObject() 重新加锁。这点刚好 Linux pthread_cond_wait() 完全相反。
Linux 于 Windows 这点额解锁操作区别重,定得牢记。否则从 Windows 移植 Linux 条等待操作旦忘结束解锁操作,程序肯定发生死锁。
等待绝时间问题
超时线程编程个常见概念。例如,当 Linux 使 pthread_cond_timedwait() 时就需指定超时这个参数,以便这个 API 调最只被阻塞指定时间间隔。但如果第次使这个 API 时,首先需解就这个 API 当超时参数特殊性(就如本节标题所提示那样)。们首先看这个 API 定义。 pthread_cond_timedwait() 定义看清单 7 。
清单 7. pthread_cond_timedwait() 函数定义
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
参数 abstime 这里表示超时时间相关个参数,但需注意所表示个绝时间,而不个时间间隔数值,只有当系统当时间达或超过 abstime 所表示时间时,才触发超时事。这于拥有 Windows 线程经验人说能尤困惑。因 Windows 所有 API 等待参数(如 SignalObjectAndWait,等)都相时间,
假设们指定相超时时间参数如 dwMilliseconds (单位毫秒)调超时相关函数,这样就需 dwMilliseconds 转化 Linux 绝时间参数 abstime 使。常转换如清单 8 所示:
清单 8. 相时间绝时间转换实例
/* get the current time */
struct timeval now;
gettimeofday(&now, NULL);
/* add the offset to get timeout value */
abstime ->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 函数定义
int pthread_join(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_t th);
另种创建线程时就设置 detached 状态,首先始化个线程属性变量,然其设置 detached 状态,最作参数传入线程创建函数 pthread_create(),这样所创建出线程就直接处于 detached 状态。如清单 11 。
清单 11. 创建 detach 线程代码实例
………………………………… ..
pthread_t tid;
pthread_attr_t attr;
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. ACE
ACE 全称 ADAPTIVE Communication Environment,个免费,开源,面向象工具框架,以并发访问软。由于 ACE 最面向网络服务端编程,因此于线程工具库能提供全面支持。其支持全面,包括 Windows,Linux 各种版本 Unix 。 ACE 唯问题如果仅仅于线程编程,其似显得有些过于重量级。而且其较复杂配置让其部署学而言并非易事。