原文地址:
http://yuyunwu.blog.sohu.com/81487237.html
线程类的具体实现
一、将接口和实现相分离
分离接口和实现是OO设计中的重要思想。它其实反映了现实世界中抽象和具象、一般和特殊的基本现象和规律。而我们,正是基于这种思想,来完成我们类库的跨平台特性。
基本上所有的OO开发环境都提供了支持接口和实现分离的机制。很多机制是基于编程语言本身的,如可继承性和多态性,而有些机制还基于体系结构特性,比如CORBA和COM。
我们把有关线程的具体实现封装到“实现类”里面,命名为TheadImpl。实际上ThreadImpl只是一个薄层封装,为上层Thread类提供一个统一线程API调用界面。
二、包装系统的API
虽然只是一个薄层封装,我们不得不做常常是让类库设计者最头疼的一件事情---统一操作系统API的调用界面。每种操作系统都提供API,常常都是相互风格迥异,这给统一包装API界面带来很多困难---虽然同时也增加了这项工作的艺术性。设计者不得不充分调研各种对象操作系统,了解每个API的调用规格,详细归纳,慎重取舍,以设计出良好的统一包装界面。在这里系统编程经验至关重要。
虽然本系列的文章顺序是先介绍线程类Thread的接口,再来讨论ThreadImpl的具体实现,这可能会给读者带来自上而下(Top down)的设计路线的印象。而实际上,和许多工作的实际过程一样,C++类库设计也不是简单的自上而下或者自下而上(Bottom up),而是一个PDCA循环(规划 Plan-执行 DO-检查 Check-措施 Action)。据我的个人经验,每个类库在第一版设计完成后都要经过重复数次的修改,以达到一个相对稳定的版本。
以上的经验之谈是希望读者能理解和大多数工作一样C++类库设计也不是一件一蹴而就的直线性工作。下面介绍我们的对象系统的线程API---我们的包装对象。
三、盘点POSIX与Windows的线程API
和其他POSIX系统API一样,POSIX线程的API在POSIX规范 IEEE 1003系列中被定义,目前的版本为2004年版。POSIX线程提供丰富的系统调用,不过我们的线程类只使用其中的一部分。如果读者手头没有IEEE的文档也没有关系,因为POSIX线程被广泛实现于各种Linux平台,绝大多数Linux系统的手册页(Manual page)都有相关的叙述。
创建线程:
POSIX:
int pthread_create (
pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*),
void *restrict arg
);
pthread_create()创建线程并立刻执行它。
Windows:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
CreateThread()允许在dwCreationFlags参数中指定线程是否以挂起状态被创建(CREATE_SUSPENDED标志)。另外CreateThread允许通过dwStackSize指定线程初始栈大小。
这两个函数都接受一个用户例程函数指针,作为线程运行的用户代码入口。
用户例程的形式定义:
POSIX:
void * start_routine(void * param);
Windows:
DWORD WINAPI ThreadProc(LPVOID lpParameter);
设置线程初始栈:
Posix:
通过线程属性设定。
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
Windows:
在CreateThread()中指定。
判断线程死活/有效性
POSIX:
IEEE 1003中并没有明确指定判断线程死活的API。不过很多人使用pthread_kill():
int pthread_kill(pthread_t thread, int sig);
通过发送信号0来测试线程的有效性。这样的做的依据可能来源于标准IEEE 1003关于pthread_kill()的一段注释:
“Upon successful completion, the function shall return a value of zero. The pthread_kill ( ) function shall request that a signal be delivered to the specified thread. As in kill( ), if sig is zero, error checking shall be performed but no signal shall actually be sent.”
另外规范中对于pthread_kill()的返回值定义如下:
[ESRCH] No thread could be found corresponding to that specified by the given thread ID.
[EINVAL] The value of the sig argument is an invalid or unsupported signal number.
调用pthread_kill,发送信号0,检测pthread_kill的返回值。如果pthread_t型的ID指向一个有效并且执行中的线程,则pthread_kill应该返回0;如果是一个无效ID或者线程已经结束,则pthread_kill应该返回ESRCH。
这看起来是个不错的办法,可惜笔者在实践中发现,在某些POSIX实现(具体的说是Cygwin的某些版本)中,调用pthread_kill并传递一个无效的线程ID将直接导致指针操作异常而使程序异常结束。
笔者发现使用另一个API好像更为安全:
int pthread_getschedparam(
pthread_t thread,
int *restrict policy,
struct sched_param *restrict param
);
pthread_getschedparam()有类似的调用语义规范:
“RETURN VALUE
If successful, the pthread_getschedparam( ) and pthread_setschedparam( ) functions shall return zero; otherwise, an error number shall be returned to indicate the error.
The pthread_getschedparam() function may fail if:
[ESRCH] The value specified by thread does not refer to an existing thread.”
所以,在我们的版本中,用pthread_getschedparam取代pthread_kill来测试线程死活。pthread_getschedparam的这一用途在笔者所测试的Cygwin和Linux版本中发挥正常。
Windows:
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
Windows编程的规范做法,是调用WaitForSingleObject,指定等待时间为0。如果返回值为WAIT_TIMEOUT则说明线程为有效,否则为无效线程HANDLE或者线程已经结束。
线程的睡眠函数:
POSIX:
unsigned sleep(unsigned seconds);
int usleep(useconds_t useconds);
这两个API提供不同时间粒度的睡眠。我们可以把它们组合起来以实现毫秒级粒度的睡眠功能。另外某些老版本的Linux实现中把sleep()和usleep()解释为进程级别的睡眠API---这已经过时了。根据IEEE 1003 2004的说明:
“The sleep( ) function shall cause the calling thread to be suspended from execution until either the number of realtime seconds specified by the argument seconds has elapsed or a signal is delivered to the calling thread and its action is to invoke a signal-catching function or to terminate the process.”
usleep也有内似的说明。目前的流行Linux版本的sleep和usleep都是线程级别的。
Windows:
VOID Sleep(DWORD dwMilliseconds);
强行结束线程:
POSIX:
int pthread_cancel(pthread_t thread);
Windows:
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
程序员应该尽量避免调用强行结束线程的API,尤其是在C++程序设计中。我们将在后续的文章中解释其原因。
四、完成线程实现类的代码
下面给出POSIX线程实现类代码。代码中使用了Java风格的注释。
//---- ThreadImpl_Linux.h----
#ifndef THREAD_IMPL_LINUX_H
#define THREAD_IMPL_LINUX_H
#include <pthread.h>
#include <unistd.h>
class Thread;
/**
* Linux(POSIX)版本的线程实现类。
*/
class ThreadImpl
{
public:
static const int WAIT_INFINITE = -1;
static const int INVALID_THREAD_ID = 0;
static const size_t DEFAULT_STACK_SIZE = 0;
static const int WAIT_TIME_SLICE = 10;
public:
/**
* 线程实现类的内部结构体。包含线程的平台相关信息。
*/
struct ThreadStruct {
pthread_t tThread;
ThreadStruct()
:
tThread(INVALID_THREAD_ID)
{
}
~ThreadStruct() {
}
};
public:
typedef int (* THREAD_FUNCTION) (Thread *pParam);
typedef void * (*POSIX_THREAD_ROUTINE) (void *pParam);
public:
/**
* 创建线程。
* @param ts线程实现类的内部结构体型变量。
* @param thread_func 用户例程函数指针。
* @param cbStackSize 线程初始栈大小,字节单位。
* @param pThread 绑定的Thread对象指针。
* @return 调用成功返回true,失败返回false。
*/
static bool CreateThread(ThreadStruct & ts,
THREAD_FUNCTION thread_func,
size_t cbStackSize,
Thread * pThread) {
pthread_attr_t *pAttr = NULL;
pthread_attr_t attr;
if(cbStackSize != DEFAULT_STACK_SIZE) {
if(0 != pthread_attr_init(&attr)) {
return false;
}
if(0 != pthread_attr_setstacksize(&attr, cbStackSize)) {
pthread_attr_destroy(&attr);
return false;
}
pAttr = &attr;
}
int iRes = pthread_create(&(ts.tThread),
pAttr,
(POSIX_THREAD_ROUTINE)thread_func,
(void*)pThread);
if(NULL != pAttr) {
pthread_attr_destroy(&attr);
}
if(0 != iRes) {
return false;
}
pthread_detach(ts.tThread);
return true;
}
/**
* 销毁线程实现类的内部结构体。
* @param ts 线程实现类的内部结构体型变量。
*/
static void DestroyThread(ThreadStruct & ts) {
}
/**
* 在指定时间内等待线程结束。
* @param ts 线程实现类的内部结构体。
* @param iTimeout 指定的等待时间。
* @return 线程在指定时间内结束的情况下返回true,否则返回false。
* 如果传入的线程结构体未关联到有效线程,则返回true。
*/
static bool WaitForThreadEnd(const ThreadStruct & ts, int iTimeout) {
int iDelta = WAIT_TIME_SLICE;
int iTotal = iTimeout;
if(iTimeout == WAIT_INFINITE) { // Cause to do unlimited loop.
iDelta = 0;
iTotal = 1;
}
for(int i=0; i<iTotal; i+=iDelta) {
if(!IsAlive(ts)) {
return true;
} else {
Sleep(WAIT_TIME_SLICE);
}
}
return false;
}
/**
* 强行结束线程。
* @param ts 线程实现类的内部结构体型变量。
*/
static void TerminateThread(ThreadStruct & ts) {
::pthread_cancel(ts.tThread);
}
/**
* 判断线程死活。
* @param ts 线程实现类的内部结构体型变量。
* @return 线程为有效则返回true,否则返回false。
*/
static bool IsAlive(const ThreadStruct & ts) {
int iPolicy;
struct sched_param sp;
int iRes = pthread_getschedparam(ts.tThread, &iPolicy, &sp);
if(0 == iRes) {
return true;
} else {
return false;
}
}
/**
* 获取线程ID。该ID在进程域内唯一。
* @param ts线程实现类的内部结构体型变量。
*/
static int GetThreadId(const ThreadStruct & ts) {
return (int) ts.tThread;
}
/**
* 导致调用线程被挂起若干时间。
* @param iMs 线程挂起时间,毫秒单位。
*/
static void Sleep(int iMs) {
int iS = iMs / 1000;
int iUs = (iMs % 1000) * 1000;
if(iS > 0) {
sleep(iS);
}
if(iUs > 0) {
usleep(iUs);
}
return;
}
private:
// 禁止构造函数。
ThreadImpl();
// 禁止拷贝构造函数。
ThreadImpl(const ThreadImpl &) throw();
// 禁止赋值操作符。
void operator=(const ThreadImpl &);
}; // class ThreadImpl
#endif // #ifndef ThreadImpl_LINUX_H
//----EOF----
Windows的线程实现类的代码如下:
//----ThreadImpl_WIN32.h----
#ifndef THREAD_IMPL_WIN32_H
#define THREAD_IMPL_WIN32_H
#include <Windows.h>
class Thread;
/**
* Windows 32位平台版本的线程实现类。
*/
class ThreadImpl
{
public:
static const int WAIT_INFINITE = -1;
static const int INVALID_THREAD_ID = 0;
static const size_t DEFAULT_STACK_SIZE = 0;
public:
/**
* 线程实现类的内部结构体。包含线程的平台相关信息。
*/
struct ThreadStruct {
HANDLE hThread;
DWORD dwThreadId;
ThreadStruct()
:
hThread(NULL),
dwThreadId(INVALID_THREAD_ID)
{
}
void Cleanup() {
if(hThread != NULL) {
CloseHandle(hThread);
hThread = NULL;
}
}
~ThreadStruct() {
Cleanup();
}
};
public:
typedef int (* THREAD_FUNCTION) (Thread *pParam);
public:
/**
* 创建线程。
* @param ts线程实现类的内部结构体型变量。
* @param thread_func 用户例程函数指针。
* @param cbStackSize 线程初始栈大小,字节单位。
* @param pThread 绑定的Thread对象指针。
* @return 调用成功返回true,失败返回false。
*/
static bool CreateThread(ThreadStruct & ts,
THREAD_FUNCTION thread_func,
size_t cbStackSize,
Thread * pThread) {
ts.hThread = ::CreateThread(NULL,
(DWORD) cbStackSize,
(LPTHREAD_START_ROUTINE) thread_func,
(LPVOID) pThread,
0,
&ts.dwThreadId);
if(ts.hThread == NULL) {
return false;
}
return true;
}
/**
* 销毁线程实现类的内部结构体。
* @param ts 线程实现类的内部结构体型变量。
*/
static void DestroyThread(ThreadStruct & ts) {
ts.Cleanup();
}
/**
* 在指定时间内等待线程结束。
* @param ts 线程实现类的内部结构体。
* @param iTimeout 指定的等待时间。
* @return 线程在指定时间内结束的情况下返回true,否则返回false。
* 如果传入的线程结构体未关联到有效线程,则返回true。
*/
static bool WaitForThreadEnd(const ThreadStruct & ts, int iTimeout) {
DWORD dwTO = (DWORD) iTimeout;
if(iTimeout == WAIT_INFINITE) {
dwTO = INFINITE;
}
DWORD dwRes = WaitForSingleObject(ts.hThread, dwTO);
if(dwRes == WAIT_TIMEOUT) {
return false;
} else {
return true;
}
}
/**
* 强行结束线程。
* @param ts 线程实现类的内部结构体型变量。
*/
static void TerminateThread(const ThreadStruct & ts) {
::TerminateThread(ts.hThread, -1);
}
/**
* 判断线程死活。
* @param ts 线程实现类的内部结构体型变量。
* @return 线程为有效则返回true,否则返回false。
*/
static bool IsAlive(const ThreadStruct & ts) {
if(NULL == ts.hThread) {
return false;
}
DWORD dwRes = WaitForSingleObject(ts.hThread, 0);
if(WAIT_TIMEOUT == dwRes) {
return true;
} else if(WAIT_OBJECT_0 == dwRes || WAIT_FAILED == dwRes) {
return false;
} else {
return false;
}
}
/**
* 获取线程ID。该ID在进程域内唯一。
* @param ts线程实现类的内部结构体型变量。
*/
static int GetThreadId(const ThreadStruct & ts) {
return (int) ts.dwThreadId;
}
/**
* 导致调用线程被挂起若干时间。
* @param iMs 线程挂起时间,毫秒单位。
*/
static void Sleep(int iMs) {
return ::Sleep(iMs);
}
private:
// 禁止构造函数。
ThreadImpl();
// 禁止拷贝构造函数。
ThreadImpl(const ThreadImpl &) throw();
// 禁止赋值操作符。
void operator=(const ThreadImpl &);
}; // class ThreadImpl
#endif // #ifndef ThreadImpl_WIN32_H
//----EOF----
对上面的代码稍微做一点说明。
Linux版本的CreateThread()中,创建完线程后立即调用pthread_detach(),以通知POSIX线程库可以在该线程结束时立即释放线程资源。注意到我们不使用pthread_join()来等待线程结束。POSIX的现程模型使用pthread_join()来实现简单的线程调度,在笔者看来用起来很不方便,因为pthread_join()只能是无限等待,不提供超时机制,大大降低了程序设计的灵活性。
为了实现带超时的线程等待函数,如我们的WaitForThreadEnd(),我们不得不把等待时间分解成更小的时间片轮询。这样做损失了一点CPU资源,并稍微影响了超时判断的时间精度。
Windows版本的TerminateThread()中,我们将线程的结束代码(Exit code)一律指定为-1。我们的上层线程类不使用这个结束代码。
Linux版本和Windows版本的线程实现类都不提供线程优先级的相关功能。
线程类的具体实现
一、将接口和实现相分离
分离接口和实现是OO设计中的重要思想。它其实反映了现实世界中抽象和具象、一般和特殊的基本现象和规律。而我们,正是基于这种思想,来完成我们类库的跨平台特性。
基本上所有的OO开发环境都提供了支持接口和实现分离的机制。很多机制是基于编程语言本身的,如可继承性和多态性,而有些机制还基于体系结构特性,比如CORBA和COM。
我们把有关线程的具体实现封装到“实现类”里面,命名为TheadImpl。实际上ThreadImpl只是一个薄层封装,为上层Thread类提供一个统一线程API调用界面。
二、包装系统的API
虽然只是一个薄层封装,我们不得不做常常是让类库设计者最头疼的一件事情---统一操作系统API的调用界面。每种操作系统都提供API,常常都是相互风格迥异,这给统一包装API界面带来很多困难---虽然同时也增加了这项工作的艺术性。设计者不得不充分调研各种对象操作系统,了解每个API的调用规格,详细归纳,慎重取舍,以设计出良好的统一包装界面。在这里系统编程经验至关重要。
虽然本系列的文章顺序是先介绍线程类Thread的接口,再来讨论ThreadImpl的具体实现,这可能会给读者带来自上而下(Top down)的设计路线的印象。而实际上,和许多工作的实际过程一样,C++类库设计也不是简单的自上而下或者自下而上(Bottom up),而是一个PDCA循环(规划 Plan-执行 DO-检查 Check-措施 Action)。据我的个人经验,每个类库在第一版设计完成后都要经过重复数次的修改,以达到一个相对稳定的版本。
以上的经验之谈是希望读者能理解和大多数工作一样C++类库设计也不是一件一蹴而就的直线性工作。下面介绍我们的对象系统的线程API---我们的包装对象。
三、盘点POSIX与Windows的线程API
和其他POSIX系统API一样,POSIX线程的API在POSIX规范 IEEE 1003系列中被定义,目前的版本为2004年版。POSIX线程提供丰富的系统调用,不过我们的线程类只使用其中的一部分。如果读者手头没有IEEE的文档也没有关系,因为POSIX线程被广泛实现于各种Linux平台,绝大多数Linux系统的手册页(Manual page)都有相关的叙述。
创建线程:
POSIX:
int pthread_create (
pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*),
void *restrict arg
);
pthread_create()创建线程并立刻执行它。
Windows:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
CreateThread()允许在dwCreationFlags参数中指定线程是否以挂起状态被创建(CREATE_SUSPENDED标志)。另外CreateThread允许通过dwStackSize指定线程初始栈大小。
这两个函数都接受一个用户例程函数指针,作为线程运行的用户代码入口。
用户例程的形式定义:
POSIX:
void * start_routine(void * param);
Windows:
DWORD WINAPI ThreadProc(LPVOID lpParameter);
设置线程初始栈:
Posix:
通过线程属性设定。
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
Windows:
在CreateThread()中指定。
判断线程死活/有效性
POSIX:
IEEE 1003中并没有明确指定判断线程死活的API。不过很多人使用pthread_kill():
int pthread_kill(pthread_t thread, int sig);
通过发送信号0来测试线程的有效性。这样的做的依据可能来源于标准IEEE 1003关于pthread_kill()的一段注释:
“Upon successful completion, the function shall return a value of zero. The pthread_kill ( ) function shall request that a signal be delivered to the specified thread. As in kill( ), if sig is zero, error checking shall be performed but no signal shall actually be sent.”
另外规范中对于pthread_kill()的返回值定义如下:
[ESRCH] No thread could be found corresponding to that specified by the given thread ID.
[EINVAL] The value of the sig argument is an invalid or unsupported signal number.
调用pthread_kill,发送信号0,检测pthread_kill的返回值。如果pthread_t型的ID指向一个有效并且执行中的线程,则pthread_kill应该返回0;如果是一个无效ID或者线程已经结束,则pthread_kill应该返回ESRCH。
这看起来是个不错的办法,可惜笔者在实践中发现,在某些POSIX实现(具体的说是Cygwin的某些版本)中,调用pthread_kill并传递一个无效的线程ID将直接导致指针操作异常而使程序异常结束。
笔者发现使用另一个API好像更为安全:
int pthread_getschedparam(
pthread_t thread,
int *restrict policy,
struct sched_param *restrict param
);
pthread_getschedparam()有类似的调用语义规范:
“RETURN VALUE
If successful, the pthread_getschedparam( ) and pthread_setschedparam( ) functions shall return zero; otherwise, an error number shall be returned to indicate the error.
The pthread_getschedparam() function may fail if:
[ESRCH] The value specified by thread does not refer to an existing thread.”
所以,在我们的版本中,用pthread_getschedparam取代pthread_kill来测试线程死活。pthread_getschedparam的这一用途在笔者所测试的Cygwin和Linux版本中发挥正常。
Windows:
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
Windows编程的规范做法,是调用WaitForSingleObject,指定等待时间为0。如果返回值为WAIT_TIMEOUT则说明线程为有效,否则为无效线程HANDLE或者线程已经结束。
线程的睡眠函数:
POSIX:
unsigned sleep(unsigned seconds);
int usleep(useconds_t useconds);
这两个API提供不同时间粒度的睡眠。我们可以把它们组合起来以实现毫秒级粒度的睡眠功能。另外某些老版本的Linux实现中把sleep()和usleep()解释为进程级别的睡眠API---这已经过时了。根据IEEE 1003 2004的说明:
“The sleep( ) function shall cause the calling thread to be suspended from execution until either the number of realtime seconds specified by the argument seconds has elapsed or a signal is delivered to the calling thread and its action is to invoke a signal-catching function or to terminate the process.”
usleep也有内似的说明。目前的流行Linux版本的sleep和usleep都是线程级别的。
Windows:
VOID Sleep(DWORD dwMilliseconds);
强行结束线程:
POSIX:
int pthread_cancel(pthread_t thread);
Windows:
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
程序员应该尽量避免调用强行结束线程的API,尤其是在C++程序设计中。我们将在后续的文章中解释其原因。
四、完成线程实现类的代码
下面给出POSIX线程实现类代码。代码中使用了Java风格的注释。
//---- ThreadImpl_Linux.h----
#ifndef THREAD_IMPL_LINUX_H
#define THREAD_IMPL_LINUX_H
#include <pthread.h>
#include <unistd.h>
class Thread;
/**
* Linux(POSIX)版本的线程实现类。
*/
class ThreadImpl
{
public:
static const int WAIT_INFINITE = -1;
static const int INVALID_THREAD_ID = 0;
static const size_t DEFAULT_STACK_SIZE = 0;
static const int WAIT_TIME_SLICE = 10;
public:
/**
* 线程实现类的内部结构体。包含线程的平台相关信息。
*/
struct ThreadStruct {
pthread_t tThread;
ThreadStruct()
:
tThread(INVALID_THREAD_ID)
{
}
~ThreadStruct() {
}
};
public:
typedef int (* THREAD_FUNCTION) (Thread *pParam);
typedef void * (*POSIX_THREAD_ROUTINE) (void *pParam);
public:
/**
* 创建线程。
* @param ts线程实现类的内部结构体型变量。
* @param thread_func 用户例程函数指针。
* @param cbStackSize 线程初始栈大小,字节单位。
* @param pThread 绑定的Thread对象指针。
* @return 调用成功返回true,失败返回false。
*/
static bool CreateThread(ThreadStruct & ts,
THREAD_FUNCTION thread_func,
size_t cbStackSize,
Thread * pThread) {
pthread_attr_t *pAttr = NULL;
pthread_attr_t attr;
if(cbStackSize != DEFAULT_STACK_SIZE) {
if(0 != pthread_attr_init(&attr)) {
return false;
}
if(0 != pthread_attr_setstacksize(&attr, cbStackSize)) {
pthread_attr_destroy(&attr);
return false;
}
pAttr = &attr;
}
int iRes = pthread_create(&(ts.tThread),
pAttr,
(POSIX_THREAD_ROUTINE)thread_func,
(void*)pThread);
if(NULL != pAttr) {
pthread_attr_destroy(&attr);
}
if(0 != iRes) {
return false;
}
pthread_detach(ts.tThread);
return true;
}
/**
* 销毁线程实现类的内部结构体。
* @param ts 线程实现类的内部结构体型变量。
*/
static void DestroyThread(ThreadStruct & ts) {
}
/**
* 在指定时间内等待线程结束。
* @param ts 线程实现类的内部结构体。
* @param iTimeout 指定的等待时间。
* @return 线程在指定时间内结束的情况下返回true,否则返回false。
* 如果传入的线程结构体未关联到有效线程,则返回true。
*/
static bool WaitForThreadEnd(const ThreadStruct & ts, int iTimeout) {
int iDelta = WAIT_TIME_SLICE;
int iTotal = iTimeout;
if(iTimeout == WAIT_INFINITE) { // Cause to do unlimited loop.
iDelta = 0;
iTotal = 1;
}
for(int i=0; i<iTotal; i+=iDelta) {
if(!IsAlive(ts)) {
return true;
} else {
Sleep(WAIT_TIME_SLICE);
}
}
return false;
}
/**
* 强行结束线程。
* @param ts 线程实现类的内部结构体型变量。
*/
static void TerminateThread(ThreadStruct & ts) {
::pthread_cancel(ts.tThread);
}
/**
* 判断线程死活。
* @param ts 线程实现类的内部结构体型变量。
* @return 线程为有效则返回true,否则返回false。
*/
static bool IsAlive(const ThreadStruct & ts) {
int iPolicy;
struct sched_param sp;
int iRes = pthread_getschedparam(ts.tThread, &iPolicy, &sp);
if(0 == iRes) {
return true;
} else {
return false;
}
}
/**
* 获取线程ID。该ID在进程域内唯一。
* @param ts线程实现类的内部结构体型变量。
*/
static int GetThreadId(const ThreadStruct & ts) {
return (int) ts.tThread;
}
/**
* 导致调用线程被挂起若干时间。
* @param iMs 线程挂起时间,毫秒单位。
*/
static void Sleep(int iMs) {
int iS = iMs / 1000;
int iUs = (iMs % 1000) * 1000;
if(iS > 0) {
sleep(iS);
}
if(iUs > 0) {
usleep(iUs);
}
return;
}
private:
// 禁止构造函数。
ThreadImpl();
// 禁止拷贝构造函数。
ThreadImpl(const ThreadImpl &) throw();
// 禁止赋值操作符。
void operator=(const ThreadImpl &);
}; // class ThreadImpl
#endif // #ifndef ThreadImpl_LINUX_H
//----EOF----
Windows的线程实现类的代码如下:
//----ThreadImpl_WIN32.h----
#ifndef THREAD_IMPL_WIN32_H
#define THREAD_IMPL_WIN32_H
#include <Windows.h>
class Thread;
/**
* Windows 32位平台版本的线程实现类。
*/
class ThreadImpl
{
public:
static const int WAIT_INFINITE = -1;
static const int INVALID_THREAD_ID = 0;
static const size_t DEFAULT_STACK_SIZE = 0;
public:
/**
* 线程实现类的内部结构体。包含线程的平台相关信息。
*/
struct ThreadStruct {
HANDLE hThread;
DWORD dwThreadId;
ThreadStruct()
:
hThread(NULL),
dwThreadId(INVALID_THREAD_ID)
{
}
void Cleanup() {
if(hThread != NULL) {
CloseHandle(hThread);
hThread = NULL;
}
}
~ThreadStruct() {
Cleanup();
}
};
public:
typedef int (* THREAD_FUNCTION) (Thread *pParam);
public:
/**
* 创建线程。
* @param ts线程实现类的内部结构体型变量。
* @param thread_func 用户例程函数指针。
* @param cbStackSize 线程初始栈大小,字节单位。
* @param pThread 绑定的Thread对象指针。
* @return 调用成功返回true,失败返回false。
*/
static bool CreateThread(ThreadStruct & ts,
THREAD_FUNCTION thread_func,
size_t cbStackSize,
Thread * pThread) {
ts.hThread = ::CreateThread(NULL,
(DWORD) cbStackSize,
(LPTHREAD_START_ROUTINE) thread_func,
(LPVOID) pThread,
0,
&ts.dwThreadId);
if(ts.hThread == NULL) {
return false;
}
return true;
}
/**
* 销毁线程实现类的内部结构体。
* @param ts 线程实现类的内部结构体型变量。
*/
static void DestroyThread(ThreadStruct & ts) {
ts.Cleanup();
}
/**
* 在指定时间内等待线程结束。
* @param ts 线程实现类的内部结构体。
* @param iTimeout 指定的等待时间。
* @return 线程在指定时间内结束的情况下返回true,否则返回false。
* 如果传入的线程结构体未关联到有效线程,则返回true。
*/
static bool WaitForThreadEnd(const ThreadStruct & ts, int iTimeout) {
DWORD dwTO = (DWORD) iTimeout;
if(iTimeout == WAIT_INFINITE) {
dwTO = INFINITE;
}
DWORD dwRes = WaitForSingleObject(ts.hThread, dwTO);
if(dwRes == WAIT_TIMEOUT) {
return false;
} else {
return true;
}
}
/**
* 强行结束线程。
* @param ts 线程实现类的内部结构体型变量。
*/
static void TerminateThread(const ThreadStruct & ts) {
::TerminateThread(ts.hThread, -1);
}
/**
* 判断线程死活。
* @param ts 线程实现类的内部结构体型变量。
* @return 线程为有效则返回true,否则返回false。
*/
static bool IsAlive(const ThreadStruct & ts) {
if(NULL == ts.hThread) {
return false;
}
DWORD dwRes = WaitForSingleObject(ts.hThread, 0);
if(WAIT_TIMEOUT == dwRes) {
return true;
} else if(WAIT_OBJECT_0 == dwRes || WAIT_FAILED == dwRes) {
return false;
} else {
return false;
}
}
/**
* 获取线程ID。该ID在进程域内唯一。
* @param ts线程实现类的内部结构体型变量。
*/
static int GetThreadId(const ThreadStruct & ts) {
return (int) ts.dwThreadId;
}
/**
* 导致调用线程被挂起若干时间。
* @param iMs 线程挂起时间,毫秒单位。
*/
static void Sleep(int iMs) {
return ::Sleep(iMs);
}
private:
// 禁止构造函数。
ThreadImpl();
// 禁止拷贝构造函数。
ThreadImpl(const ThreadImpl &) throw();
// 禁止赋值操作符。
void operator=(const ThreadImpl &);
}; // class ThreadImpl
#endif // #ifndef ThreadImpl_WIN32_H
//----EOF----
对上面的代码稍微做一点说明。
Linux版本的CreateThread()中,创建完线程后立即调用pthread_detach(),以通知POSIX线程库可以在该线程结束时立即释放线程资源。注意到我们不使用pthread_join()来等待线程结束。POSIX的现程模型使用pthread_join()来实现简单的线程调度,在笔者看来用起来很不方便,因为pthread_join()只能是无限等待,不提供超时机制,大大降低了程序设计的灵活性。
为了实现带超时的线程等待函数,如我们的WaitForThreadEnd(),我们不得不把等待时间分解成更小的时间片轮询。这样做损失了一点CPU资源,并稍微影响了超时判断的时间精度。
Windows版本的TerminateThread()中,我们将线程的结束代码(Exit code)一律指定为-1。我们的上层线程类不使用这个结束代码。
Linux版本和Windows版本的线程实现类都不提供线程优先级的相关功能。