1.从一个Thread类图出发
- (1)下面的Thread类是一个抽象类:不能实例化对象;
下面的TestThread是一个派生类,具体类 - (2)每个Thread类都有自己的执行体,可以用Run方法来表示,不同的TestThread类执行体是不一样的。
可以将该方法提升到基类做一个抽象的接口,就是那个纯虚函数Run()。
2.用例解析
- 使用cmake编译
- 目录如下:
- build.sh
set -x
SOURCE_DIR=`pwd`
BUILD_DIR=${BUILD_DIR:-../build}
mkdir -p $BUILD_DIR \
&& cd $BUILD_DIR \
&& cmake $SOURCE_DIR \
&& make $*
//直接输入./build.sh进行编译
cmake_minimum_required(VERSION 2.6)
project(pas CXX)
set(CXX_FLAGS -g -Wall)
set(CMAKE_CXX_COMPILER "g++")
string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}")
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(Thread_test Thread_test.cpp Thread.cpp)//生成的可执行程序的名称:Thread_test
target_link_libraries(Thread_test pthread)//链接pthread库
using namespace std;
//派生类公有继承Thread基类
//只要继承Thread抽象类,那么不同的线程体就有不同的执行体,即Run()接口
//面向对象编程会暴露Thread抽象类,而基于对象编程不会暴露Thread抽象类
class TestThread : public Thread
{
public:
TestThread(int count) : count_(count)
{
cout<<"TestThread ..."<<endl;
}
~TestThread()
{
cout<<"~TestThread ..."<<endl;
}
private:
void Run()//成员函数Run()可以访问数据成员count_
{
while (count_--)
{
cout<<"this is a test ..."<<endl;
sleep(1);//
}
}
int count_;
};
int main(void)
{
/*
TestThread t(5);//派生类对象
t.Start();//成员函数Start()第一个参数隐含了一个this指针,指向派生类对象本身,相当于&t
t.Join();//等待pthread子线程函数退出,防止主线程退出了,子线程还没有运行
//////////////////////////////////
t.Run();//直接写这样的代码表示在主线程当中运行,虽然结果一样
//////////////////////////////////
*/
/*
注意:线程对象的生命周期和线程的生命周期时不一样的!!
线程生命周期的销毁,是程序结束的时候;而且线程执行完毕,线程对象可能还未被销毁。
所以,如何实现线程执行完毕,线程对象能够自动销毁?
答案:所以该对象是动态创建对象才可以,所以new个对象
*/
TestThread* t2 = new TestThread(5);
t2->SetAutoDelete(true);
t2->Start();
t2->Join();
//死循环的作用:防止主线程结束了,子线程还没有执行
for (; ; )
pause();
return 0;
}
using namespace std;
Thread::Thread() : autoDelete_(false)
{
cout<<"Thread ..."<<endl;
}
Thread::~Thread()
{
cout<<"~Thread ..."<<endl;
}
void Thread::Start()
{
//这里的this指针指向的是派生类,因为我们后面肯定用的是派生类
pthread_create(&threadId_, NULL, ThreadRoutine, this);//线程id,线程属性,线程的入口函数,入口函数的参数用this指针传递
//这里run函数为啥不能作为入口函数??因为run是普通的成员函数,隐含的第一个参数是Thread*,即this指针,
//调用的时候是thiscall的约定,某个寄存器会保存this指针,而
//ThreadRoutine是普通函数的调用约定,可以编写全局的函数,也可以用static 成员函数,这样的话,
//调用就不会隐含传递一个this指针过来!!
}
void Thread::Join()
{
pthread_join(threadId_, NULL);
}
//因为run()才是线程要执行的执行体,所以这里要调用run方法。但是不能直接调用,因为ThreadRoutine是静态成员函数,
//不能调用非静态的成员函数run()!!!因为他没有this指针,
//上面的this指针已经传递到ThreadRoutine的入口arg
void* Thread::ThreadRoutine(void* arg)
{
//main中的t.Start();表明:这是基类指针指向派生类对象,基类指针调用的是派生类实现的虚函数
//(1)这就是虚函数的多态,若pthread类是库实现的,main中创建线程,相当于库回调了Run()方法,即(2)虚函数具有回调功能
Thread* thread = static_cast<Thread*>(arg);//将派生类指针转化为基类指针,因为静态成员函数不能访问非静态的成员
//基类指针不要直接访问private函数,但是这里能用
thread->Run();
//基类指针不要指针访问private成员,但是这里能用
if (thread->autoDelete_)//这里也要注意:静态成员函数不能访问非静态的成员,所以要通过指针来访问
delete thread;//线程执行完毕了,应该销毁它,后面调用的时候,这里的thread指的是派生类对象,即t2
return NULL;
}
void Thread::SetAutoDelete(bool autoDelete)
{
autoDelete_ = autoDelete;
}
//Thread抽象基类
class Thread
{
public:
Thread();//构造函数
virtual ~Thread();//必须是纯虚析构函数,因为该类是作为多态基类,若不使用多态基类,可以不使用纯虚析构函数。
//若不用的话,会导致派生类的资源释放的时候不完全
void Start();//线程的启动方法
void Join();//线程的join函数,主要调用pthread_join
void SetAutoDelete(bool autoDelete);
private:
static void* ThreadRoutine(void* arg);//加了static就没有隐含的this指针了!!
//纯虚函数别定义成private的,但是这里能用而已
virtual void Run() = 0;//不同的线程函数的执行体,线程的抽象接口
pthread_t threadId_;//线程id
bool autoDelete_;//比如实现一个线程池,我们希望线程如果执行结束,能自动销毁线程对象
};
- 测试: