在 C++11 发布前,C++语言本身并不支持线程,而是通过 pthread 库来进行支持的,C++11发布后,其语言本身已经支持了线程,且是通过线程类的方式进行提供,使用方法可能参考此篇文章:c++11线程类 。现在C++11及之后版本已经提供了线程类来使用线程,但我们在编译的时候还是需要添加 -lpthread 链接(也许其线程类实现还是得依赖 pthread 库吧,没看过源码还不清楚),那如果不使用c++提供的线程类,我们在实际项目中是如何封装的线程类呢?
以下代码先以 muduo 库里的线程类进行一下讲解,看它是如何封装线程类的,如:
#ifndef __UNI_THREAD_H__
#define __UNI_THREAD_H__
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <pthread.h>
class CThread: public boost::noncopyable
{
public:
typedef boost::function0<void> threadProc;
explicit CThread(const threadProc& proc, std::string threaName);
~CThread();
void createThread();
bool started()const
{
return mStarted;
}
pid_t tid() const
{
return mTid;
}
const std::string& name()const
{
return mName;
}
int join();
private:
static void* startThread(void* arg);
void runInThread();
private:
bool mStarted;
pthread_t mPthreadId;
pid_t mTid;
threadProc funcProc;
std::string mName;
};
#endif
这是一个线程类的声明,我们看到此类构造函数里就可以绑定要执行的线程函数。它的实现是这样的:
#include <assert.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include "Thread.h"
#include "CurrentThread.h"
namespace detail
{
pid_t gettid()
{
return static_cast<pid_t>(::syscall(SYS_gettid));
}
};
/*****************************
* namespace CurrentThread
* **************************/
namespace CurrentThread
{
__thread int tCachedTid = 0;
__thread char tTidString[32];
__thread const char *tThreadName = "unknow";
};
void CurrentThread::cacheTid()
{
///第一次调用时会调用系统函数
if(tCachedTid == 0)
{
tCachedTid = detail::gettid();
snprintf(tTidString, sizeof(tTidString), "%5d ", tCachedTid);
}
}
bool CurrentThread::isMainThread()
{
return tid() == ::getpid();
}
/*****************************
* CThread
* **************************/
CThread::CThread(const threadProc& proc, std::string threaName):
mStarted(false),
mPthreadId(-1),
funcProc(proc),
mName(threaName)
{
}
CThread::~CThread()
{
}
void CThread::createThread()
{
assert(!mStarted);
mStarted = true;
errno = pthread_create(&mPthreadId, NULL, startThread, this);
if(errno < 0)
{
}
}
void* CThread::startThread(void* arg)
{
CThread *thread = static_cast<CThread*>(arg);
thread->runInThread();
return nullptr;
}
void CThread::runInThread()
{
mTid = CurrentThread::tid();
if(funcProc)
{
funcProc();
}
}
int CThread::join()
{
assert(mStarted);
return pthread_join(mPthreadId, nullptr);
}
可以看到在 createThread() 成员函数里调用了 pthread_create() 进行线程的创建,而且线程函数是此类的一个 static 函数 startThread(void* arg),为什么是 static 类型函数呢?因为函数 pthread_create() 的第 3 个参数是一个函数指针,且是 C 语言类型的函数指针,这里无法传入类的成员函数。实际工作中遇到的是另外一种实现方法,后面会有介绍。所以使用时,只要定义一个CThread 对象,然后调用 createThread() 即可,如:
在 调用 CAsyncLogging::start() 后,线程即刻创建起来,且执行线程函数 threadProc()。
实际工作中遇到线程类是这样的,列举片段:
// 线程类的声明
class CThread
{
///其他成员函数
bool createThread();
/// 线程执行体,是一个纯虚函数,派生的线程类中必须实现此函数,实现各自的行为。
virtual void threadProc() = 0;
}
这里最重要的是声明了一个纯虚函数,说明这个类是一个抽象类,只能用作基类。它也有一个 public 成员函数 createThread(),通过此函数创建线程,但这里比上面那个 createThread() 复杂一点,它里面会设置线程的属性、调度策略、线程优先级、线程栈大小等。
ret = pthread_create(&mInternal->mHandle, &attr, threadBody, (void *)mInternal);
它的线程函数也是一个全局函数,最后一个是一个内部的对象,保存着这个线程的相关信息。在线程函数里首先会把这个参数强转成指定类型,然后再进行调用。
pInternal->mOwner->threadProc();
而这个mOwner 就是在CThread 的构造函数里被赋为了 this,就是CThread对象,派生类只要实现 virtual void threadProc(),然后调用 createThread() 后就可以调用到自己的 threadProc() 函数了,这里用到的就是类的虚函数特性了。使用起来也很方便:
#include "Thread.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
class CMyThread: public CThread
{
public:
CMyThread();
~CMyThread();
virtual void threadProc();
};
CMyThread::CMyThread(): CThread("myThread")
{
}
CMyThread::~CMyThread()
{
}
void CMyThread::threadProc()
{
int count = 5;
while(count--)
{
sleep(1);
printf("in CMyThread\n");
}
}
int main()
{
CMyThread *myThread = new CMyThread();
myThread->createThread();
sleep(7);
return 0;
}
派生类里只要实现 threadProc() 即可,运行结果如: