本文通过一个简单的线程类对面向对象和基于对象两种不同的编程风格进行说明。
面向对象编程
设计一个Thread基类,这个类中含有一个纯虚函数Thread::run(),然后在测试程序中派生一个子类,并在这个子类中重写run()方法。每一个线程对应一个Thread的子类。
面向对象编程风格的Thread类图如下所示:
具体代码如下:
Thread.h
#ifndef _THREAD_H_
#define _THREAD_H_
#include <pthread.h>
class Thread
{
public:
Thread();
virtual ~Thread();
void Start();
void Join();
void SetAutoDelete(bool autoDelete); // 设置线程对象自动析构
private:
static void *ThreadRoutine(void *arg);
virtual void Run() = 0;
pthread_t threadId_;
bool autoDelete_; // new added variable
};
#endif // _THREAD_H_
Thread.cpp
#include "Thread.h"
#include <iostream>
using namespace std;
Thread::Thread() : autoDelete_(false) {
cout << "Thread ..." << endl;
}
Thread::~Thread() {
cout << "~Thread ..." << endl;
}
void Thread::Start() {
pthread_create(&threadId_, NULL, ThreadRoutine, this);
}
void Thread::Join() {
pthread_join(threadId_, NULL);
}
void *Thread::ThreadRoutine(void *arg) {
Thread *thread = static_cast<Thread *>(arg);
thread->Run();
if(thread->autoDelete_)
delete thread;
return NULL;
}
void Thread::SetAutoDelete(bool autoDelete) {
autoDelete_ = autoDelete;
}
Thread_Test.cpp
#include "Thread.h"
#include <unistd.h>
#include <iostream>
using namespace std;
class TestThread: public Thread
{
public:
TestThread(int count) : count_(count) {
cout << "TestThread ..." << endl;
}
~TestThread() {
cout << "~TestThread ..." << endl;
}
private:
void Run() {
while(count_--) {
cout << "this is a test ..." << endl;
sleep(1);
}
}
int count_;
};
int main(void)
{
/*
TestThread t(5);
t.Start();
t.Join();
*/
TestThread *t2 = new TestThread(5);
t2->SetAutoDelete(true);
t2->Start();
t2->Join();
for(; ; )
pause();
return 0;
}
如果在栈上创建线程对象,则从Thread_Test.cpp的main函数中我们会发现线程对象和所创建线程的生命周期并不相同,线程执行完毕就会退出,而线程对象会一直存在,直到程序退出时才会被销毁。那如何让线程对象在线程退出后自动销毁呢?答案是在Thread类中添加一个bool类型的成员变量autoDelete_和一个SetAutoDelete成员函数,并在堆上创建线程对象。通过SetAutoDelete我们就可以设置线程对象在线程退出后立即销毁。
下面我们分析一下上面程序的执行流程:首先创建一个子线程对象并设置自动析构,随后调用start()创建一个线程(线程的入口函数为静态函数ThreadRoutine(线程入口函数为void *func(void *)类型,普通成员函数隐含一个this参数,因此不能作为线程的入口函数))。线程真正的执行是在run()函数中进行的,而run()作为一个普通成员函数一般是无法被静态成员函数调用的,因此我们需要向线程中传递一个this指针,再通过它调用run()方法。
面向对象的编程风格有以下特点:
- 会暴露抽象类;
- 利用虚函数的多态性回调相应的接口;
- 类内部使用static函数作为线程入口函数,需要将当前线程的this指针作为pthread_create()的第四个参数传递到线程内部。
基于对象编程
令Thread为一个具体的类,其构造函数接受一个ThreadFunc对象。应用程序只需提供一个能转换为ThreadFunc的对象即可创建一份Thread实体,然后调用Thread::start()即可。
面向对象编程风格的Thread类图如下所示:
其具体实现代码如下所示:
#ifndef _THREAD_H_
#define _THREAD_H_
#include <pthread.h>
#include <boost/function.hpp>
class Thread
{
public:
typedef boost::function<void()> ThreadFunc;
explicit Thread(const ThreadFunc& func);
void Start();
void Join();
void SetAutoDelete(bool autoDelete);
private:
static void *ThreadRoutine(void *arg);
void Run();
ThreadFunc func_; // new added
pthread_t threadId_;
bool autoDelete_;
};
#endif // _THREAD_H_
Tread.cpp
#include "Thread.h"
#include <iostream>
using namespace std;
Thread::Thread(const ThreadFunc& func): func_(func), autoDelete_(false) {
}
void Thread::Start() {
pthread_create(&threadId_, NULL, ThreadRoutine, this);
}
void Thread::Join() {
pthread_join(threadId_, NULL);
}
void* Thread::ThreadRoutine(void *arg) {
Thread *thread = static_cast<Thread *>(arg);
thread->Run();
if(thread->autoDelete_)
delete thread;
return NULL;
}
void Thread::SetAutoDelete(bool autoDelete) {
autoDelete_ = autoDelete;
}
void Thread::Run() {
func_();
}
Thread_Test.cpp
#include "Thread.h"
#include <boost/bind.hpp>
#include <unistd.h>
#include <iostream>
using namespace std;
class Foo
{
public:
Foo(int count): count_(count) {
}
void MemberFun() {
while(count_--) {
cout << "this is a test ..." << endl;
sleep(1);
}
}
void MemberFun2(int x) {
while(count_--) {
cout << "x=" << x << " this is a test2 ..." << endl;
sleep(1);
}
}
int count_;
};
void ThreadFunc() {
cout << "ThreadFunc ..." << endl;
}
void ThreadFunc2(int count) {
while(count--) {
cout << "ThreadFunc2 ..." << endl;
sleep(1);
}
}
int main(void)
{
Thread t1(ThreadFunc);
Thread t2(boost::bind(ThreadFunc2, 3));
Foo foo(3);
Thread t3(boost::bind(&Foo::MemberFun, &foo));
Foo foo2(3);
Thread t4(boost::bind(&Foo::MemberFun2, &foo2, 1000));
t1.Start();
t2.Start();
t3.Start();
t4.Start();
t1.Join();
t2.Join();
t3.Join();
t4.Join();
return 0;
}
基于对象的编程风格只使用具体类,不暴露抽象类。
应用程序只需提供一个能转换为ThreadFunc的对象即可创建一份Thread实体,然后调用Thread::start()即可开始线程的执行。