多线程程序主要分为一下两个部分,1.线程部分,2.线程通讯部分
线程部分包括线程的创建,启动,运行。
线程通讯部分包括线程间的数据交换,消息发送,以及线程内部的数据交换和消息发送
一,线程部分
线程部分主要功能是线程的创建,启动和运行
在基本的linux中,线程的创建实际上只要一个函数就可以实现
int pthread_create(pthread_t *tid, const pthread_attr_t *tattr, void *(*start_routine)(void *), void *arg);
返回值:函数成功返回0。任何其他返回值都表示错误。
创建一个线程。
参数tattr中含有初始化线程所需要的属性,start_routine是线程入口函数的地址,当start_routine返回时,相应的线程就结束了。
当函数成功时,线程标示符保存在参数tid指向的内存中。
如果不指定属性对象,将其置为NULL,则创建一个缺省的线程,有如下属性:
非绑定的;
未分离的;
由一个缺省大小的堆栈;
具有和父线程一样的优先级。
注意:在创建子线程时,传给子线程的输入参数最好是由malloc()函数返回的指针或指向全局变量的指针,而不要是指向局部变量的指针。要保证子线程处理参数时,该区域仍然有效。
因为我们所说的是面向对象模式下的多线程编程,所以,势必要把线程的创建,启动,运行封装到一个线程类(class Thread)中,当我们启动自己的线程的时候,只需要继承这个类,然后重载这个类中的主函数就可以了,编写者可以把自己的精力都放到具体的业务逻辑中去。
为了封装这个类,也为了跨平台的实现,我们封装线程类用到了三个类,分别是
1. Thread类 负责线程的具体逻辑
2. OS 类 负责线程的创建,操作系统的底层调用都在这个类中,如pthread_create
3. OSFactory 类 设计模式中“简单工厂”类,负责OS的创建,可以根据不同平台创建不同的OS类
这三个类都是与平台无关的类,是最高层的抽象,只有几个最基础的方法和虚函数。
在OS类下面可以进行继承,来具体实现不同的平台(Linux,Win32,vxworks,OS2….),我们这里以Linux为例,所以在OS类之下要继承出LinuxOS,来具体实现线程的创建等函数。
在Thread类下面可以继承出自己想实现的具体功能的线程类。如A1Thread,A2Thread等。
首先来看Thread类,该类描述如下:
class
Thread

...
{
public:
Thread(); //构造函数
virtual ~Thread(); //析构函数
void ThreadEntry(); //线程入口点
protected:
virtual void Run(void) = 0; //主函数,继承类重载此方法
virtual bool Initialize(void); //初始化,继承类重载此方法
virtual void Deinitialize(void); //退出前析构,继承类重载此方法

OS *fOS; //OS对象,用来启动线程
private:
Thread(const Thread &otherInstance);
Thread & operator = (const Thread &otherInstance);
}
;
线程类构造
Thread::Thread()

...
{
fOS = OSFactory::NewOS(); //返回一个OS对象
if (fOS== NULL)

...{
printf("Fatal Error: CnlThread::CnlThread() : Failed to create OS instance! ");
}
if (!fOS->CreateThread(this,20*1024)) //使用fOS开始创建线程

...{
printf("CnlA1Thread Failed to spawn thread! ");
}
}
线程入口点:入口点中的三个函数是你唯一要根据自己的程序要求要修改的地方。
void
Thread::ThreadEntry()

...
{
bool result;
Initialize(); //初始化
Run(); //运行
Deinitialize(); //析构
}

OS类描述如下:
class
OS

...
{
public:
OS(); //构造函数
virtual ~OS(); //析构函数
virtual bool CreateThread(Thread *pThread,unsigned long StackSize=8*1024)=0;//创建线程

private:
OS(OS &otherInstance);
OS &operator =(OS &otherInstance);

protected:
Thread *fThread; //线程对象
}
;

LinuxOS从OS继承而来,描述如下:
class
LinuxOS:
public
OS

...
{
public:
LinuxOS();
virtual ~LinuxOS();
virtual bool CreateThread(Thread *pThread,unsigned long StackSize=8*1024);

private:
LinuxOS(LinuxOS &otherInstance);
LinuxOS & operator = (LinuxOS &otherInstance);

sem_t mSuspendSem;
pthread_t mThreadId;// POSIX 线程 id
pthread_attr_t mThreadAttr;// 线程属性对象
}
;

CreateThread方法负责启动一个新的线程,程序如下:
bool
LinuxOS::CreateThread(Thread
*
pThread, unsigned
long
StackSize)

...
{
int rc;
unsigned int arguments[4];
bool result;
if (pThread == NULL)

...{
printf("LinuxOS::CreateThread() : Thread pointer is NULL! Can't begin the thread... ");
return false;
}
fThread = pThread;
pthreadc=pThread; //pthreadc是全局变量,标示当前需要启动的线程类
pthread_attr_init(&mThreadAttr); //线程属性初始化
memset(arguments, 0, sizeof(arguments));
arguments[0] = (unsigned long) pThread; //参数传递
rc = pthread_create(&mThreadId, &mThreadAttr, (void * (*) (void *))ThreadEntrypoint, arguments); //启动ThreadEntrypoint函数创建实在线程

if (rc != 0)...{
printf("LinuxOS::CreateThread() : Failed to create and start the thread! ");
fThread = NULL;
mThreadId = 0;
return false;
}
pthread_detach(mThreadId);
return true;
}


ThreadEntrypoint函数描述如下,该函数不属于任何类
void
ThreadEntrypoint(unsigned
long
*
threadParm)

...
{
if (pthreadc == NULL)

...{
printf("ThreadEntrypoint: ERROR - thread pointer is NULL! Can't start the thread! ");
}
else

...{
pthreadc->ThreadEntry(); //进入到线程类的入口点,准备运行run()
}
pthread_exit(0);
}


OSFactory描述如下:
class
OSFactory

...
{
public:
OSFactory();
virtual ~OSFactory();
static OS *NewOS(); //创建相应的OS对象

private:
OSFactory(OSFactory &otherInstance);
OSFactory & operator =(OSFactory &otherInstance);
}
;

static OS *NewOS()方法描述如下:
OS
*
OSFactory::NewOS(
void
)

...
{
#ifdef (__linux__)
return new LinuxOS(); //根据需要返回不同类型的OS对象,我们这里是LinuxOS
#endif
#ifdef (__win32__)
return new Win32OS(); //这个可以根据操作系统的不同继承OS类来得到新类
#endif
}

至此,我们的面向对象多线程框架已经搭起来了,下面要做的就是你根据自己的需要,从Thread继承一个线程类,然后重载run方法,来实现你的功能。
最后,在整个程序的main函数中添加如下两句话就可以了
int
main()

...
{
ThreadA *pA;
pA=new ThreadA();
sleep(3);
ThreadB*pB;
pB=new ThreadB();
while(1)
;
}
这样,就创建了两个线程ThreadA和ThreadB,并且这两个线程可以很好的跑起来了,只是他们之间还不能通讯,通讯是我们接下来要讲的事情了。
我们看看ThreadA是怎么跑起来的啊
1.pA=new ThreadA()调用ThreadA的构造函数,由于构造函数我们没有重写,所以是调用的Thread的构造函数
2.Thread的构造函数通过调用fOS = OSFactory::NewOS()返回了一个LinuxOS对象
3. 然后构造函数继续调用fOS->CreateThread(this,20*1024) 开始创建线程,并把自己(this),也就是ThreadA传入到参数中
4.在CreateThread中pthreadc=pThread;把ThreadA赋值给全局变量pthreadc,然后调用线程创建函数pthread_create跳入到ThreadEntrypoint中,此时,线程已经起来了
5.在ThreadEntrypoint中调用pthreadc->ThreadEntry()调用了ThreadA的线程入口点函数
6. ThreadEntry没有被重载,所以依次执行Initialize();Run();Deinitialize();这三个函数,而这三个函数是ThreadA重载的,所以开始执行你自己的方法了。
按照上面的顺序我们可以知道,如果按照这个框架来跑多线程,你唯一要做的就是从Thread类继承一个你自己类,然后重载Initialize();Run();Deinitialize();来实现你具体要实现的功能,最后在main函数中new一个你的类对象就可以了。
目前这样只是能按照一个类就是一个线程的模式跑多个线程,而线程之间还不能通讯,后面我们将慢慢添加这个框架,使线程之间可以互相通讯,我们目的是多线程和面向对象。