多线程在Linux上已经有非常成熟的Pthread库支持。涉及的多线程开发的最基本概念主要包含以下三点:线程,互斥锁,条件。
其中,线程操作又分为线程的创建,退出,等待三种,互斥锁则包含四种操作:创建,销毁,加锁,解锁。条件操作有五种操作:创建,销毁,触发,广播,等待。
线程,互斥锁,条件在Linux平台上对应的API可以用表1归纳,同时也列出了Windows下的SDK
表 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下的同一线程无法对同一互斥锁进行递归加速,否则将发生死锁。
所谓递归加锁,就是在同一线程中试图对互斥锁进行两次或者两次以上的行为,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属性,基于此,以上代码稍作修改就可以很好的运行,只需要在初始化的时候外加一个设置属性就OK了。
-
pthread_mutexattr_init(&attr);
-
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP); //设置recursive属性
-
pthread_mutex_init(theMutex,&attr);
SO,建议尽量设置recursive属性以初始化Linux的互斥锁,这样既可以解决同一线程递归加锁的问题,又可以避免很多情况下死锁的发生。这样做也可以让Windows和Linux下让锁的表现统一。
Linux平台上Pthread下的条件变量状态变化模型是这样工作的:调用pthread_cond_signal()释放被条件阻塞的线程时,无论存不存在被阻塞的线程,条件都将被重新复位,下一个被条件阻塞的线程将不受影响。
超时是多线程编程的常见概念,例如,当你在Linux平台下使用pthread_cond_timedwait()时就需要制定超时这个参数,以便这个API的调用者最多只被阻塞指定的时间间隔。但是如果你是第一次使用这个API时,首先你需要了解的就是这个API当中超时参数的特殊性,我们首先来看一下这个API的定义:
-
int pthread_cond_timewait(pthread_cond_t restrict_cond,pthread_mutex_t *restrict_mutex,
-
const struct timespec *restrict abstime);
参数abstime在这里用来表示和超时时间相关的一个参数,但是需要注意的是它所表示的是一个绝对时间,而不是一个时间间隔数值,只有当系统的当前时间达到或者超过abstime所表示的时间时,才会触发超时事件。
Linux的绝对时间看似简单明了,却是开发中一个灰常隐晦的陷阱。
在Linux平台下,当处理线程结束时需要注意的一个问题就是如何让一个线程善始善终,让其所占资源得到正确释放,在Linux平台下,虽然各个线程之间是相互独立的,一个线程的终止不会去
通知或者影响其他的线程。但是已经终止的线程的资源并不会随着线程的终止而得到释放,我们需要调用pthread_join()来获得另一个线程的终止状态并且释放该线程所占的资源,pthread_join()所占清单如下:
-
int pthread_join(pthread_t th,void **thread_return);
调用该函数的线程将挂起,等待th所表示的线程的结束。thread_return是指向线程th返回值的指针。需要注意的是th所表示的线程必须是joinable的,即处于非detached状态,并且只可以有唯一的一个线程对th调用pthread_join(),如果th处于detached状态,那么对th的thread_join()调用将返回错误。
一个线程设置为detached状态可以通过两种方式实现。一种是调用pthread_detach()函数,可以将线程th设置为detached状态。一种是在创建线程的时候就将其设置为detached属性。
阅读(2705) | 评论(0) | 转发(2) |