POSIX 提供以下函数:
pthread_create():创建一个线程
pthread_exit():终止当前线程
pthread_cancel():中断另外一个线程的运行
pthread_join():阻塞当前的线程,直到另外一个线程运行结束
pthread_attr_init():初始化线程的属性
pthread_attr_setdetachstate():设置脱离状态的属性(决定这个线程在终止时是否可以被结合)
pthread_attr_getdetachstate():获取脱离状态的属性
pthread_attr_destroy():删除线程的属性
pthread_kill():向线程发送一个信号
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);//返回:成功为0 失败为错误码
我们注意第3个参数是一个函数指针,它返回void *
参数也是void *
,这就是所谓线程(第三个参数里理解为线程要做的工作)
,第四个参数则是线程的参数.
POSIX的thread默认是joinable,需要手工调用pthread_join函数来回收,也可以调用pthread_detach将其变为detachable,此时不需要手工回收线程。
下面介绍Thread的封装。
通常情况下,我们可能会这样写代码:
void *mythread(void *arg)
{
//tasks...
}
int main()
{
pthread_t tid1;
pthread_create(&tid1,NULL,mythread,NULL);
//main thread
pthread_join(tid1, NULL);
}
这里直接用函数名指代回调函数,也可以加上取地址符&
1.2线程回收:
首先得知道线程的两个状态:
Joinable
Detached
简单理解,如果一个线程是joinable的状态,那么这样的线程,就必须使用pthread_join来回收,否则程序结束时,线程所占用的资源不会释放,就会造成内存泄漏。我们通常在主进程中会阻塞调用pthread_join来等待我们的线程结束。
如果是Detached状态的线程,那么在线程结束后,资源会自动释放,POSIX pthread线程库中,提供下面函数来让线程进入Detached状态:
#include
int pthread_detach(pthread_t thread);
在我们的设计中,考虑pthread_join和pthread_detach的特性,分别对成员函数join和析构函数进程封装。
线程创建时默认情况情况下是Joinable状态。
1.3示例:
通常只需注意线程的创建与回收即可,例如:
#include
#include
#include
using namespace std;
void * child1(void * arg)
{
pthread_t tid =pthread_self();
cout<<"thread "<<(unsigned int )tid<<" enter"<
封装为一个C++ 类需要一些技巧:
1.Thread是没有拷贝构造和赋值语义的,因此也需要继承自boost::noncopyable
2.关于pthread_create的第三个参数(返回void*参数为void*的函数指针),因为成员函数会隐式包含this指针作为参数,所以加以static修饰。
3.对于pthread_create的第四个参数(void *arg作为线程参数),传入this指针方便线程调用成员函数。
4.使用join()成员函数封装pthread_join(),析构函数使用pthread_detach()处理。
5.设置run()成员函数为纯虚函数,子类重写run()方法。
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include "CCondition.h"
#include "CThreadMutex.h"
using namespace std;
/* Thread state. */
typedef enum
{
THREAD_IDLE,
#define THREAD_IDLE THREAD_IDLE
THREAD_RUNNING,
#define THREAD_RUNNING THREAD_RUNNING
THREAD_WAKEUP,
#define THREAD_WAKEUP THREAD_WAKEUP
THREAD_END_NORMAL,
#define THREAD_END_NORMAL THREAD_END_NORMAL
THREAD_END_ERROR,
#define THREAD_END_ERROR THREAD_END_ERROR
}ThreadState;
#define NOTDETACH false
#define DETACH true
class CThread : public boost::noncopyable
{
public:
//线程的构造函数
CThread()
{
this->m_ThreadID = NULL;//初始化线程ID
this->m_ThreadState = THREAD_IDLE;//初始化线程撞他
}
//线程想析构函数
virtual ~CThread()
{
if(this->m_ThreadState==THREAD_RUNNING)
{
CHECK(!pthread_detach(this->m_ThreadID));//如果线程正在运行,则让其状态为detached
}
}
//线程的任务函数(纯虚函数)
virtual void Run(void) = 0;
//线程入口(实际创建线程的地方)
void Start(void) //入口
{
CHECK(!pthread_create(&this->m_ThreadID, NULL, CThread::ThreadFunction, this));
}
//获得线程ID
int GetThreadID(void)
{
return this->m_ThreadID;
}
//让其状态为detached
bool Detach(void)
{
CHECK(!pthread_detach(this->m_ThreadID));
}
//回收线程
bool Join(void)
{
assert(this->m_ThreadState==THREAD_RUNNING);
CHECK(!pthread_join(this->m_ThreadID, NULL));//回收线程
}
protected:
static void* ThreadFunction(void* arg)
{
CThread* pt = static_cast (arg);
pt->Run();
return NULL;
}
private:
pthread_t m_ThreadID;
ThreadState m_ThreadState;//线程状态
};
这里对成员函数进行定义,run()
由于是纯虚函数,我们放到子类中重写:
这里需要说明的是:
首先,为了获得最干净的语义,Thread应该是不可复制的,所以需要继承NonCopyable。
其次,为了调用pthread_create创建线程,我们往里面注册的不能是一个成员函数,因为成员函数含有一个隐式参数(this),导致函数的指针类型并不是void *(*start_routine) (void *),所以我们采用了static函数。
static函数无法访问某一对象的成员,所以我们在调用pthread_create时,将this指针作为回调函数的参数。(即上文锁提到的第四个参数)
这个Thread不提供detach函数,因为我们在析构函数中做了如下的处理,如果Thread对象析构,线程还在运行,那么需要将Thread设置为detach状态。
大部分逻辑都是固定的,用户只需要改变run里面的代码即可,于是我们将run设置为纯虚函数,让用户继承Thread类。
所以析构函数为virtual。