在 C++11 发布前,C++语言本身并不支持线程,而是通过 pthread 库来进行支持的,C++11发布后,其语言本身已经支持了线程,且是通过线程类的方式进行提供,使用方法可能参考此篇文章:c++11线程类 。现在C++11及之后版本已经提供了线程类来使用线程,但我们在编译的时候还是需要添加 -lpthread 链接(也许其线程类实现还是得依赖 pthread 库吧,没看过源码还不清楚),那如果不使用c++提供的线程类,我们在实际项目中是如何封装的线程类呢?
以下代码先以 muduo 库里的线程类进行一下讲解,看它是如何封装线程类的,如:
- #ifndef __UNI_THREAD_H__
- #define __UNI_THREAD_H__
-
- #include
- #include
- #include
-
-
- 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
- #include
- #include
- #include
- #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
(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
- #include
- #include
-
- 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() 即可,运行结果如: