Linux多线程——封装POSIX线程库(三)线程的封装

C++封装POSIX 线程库(三)线程的封装

1.POSIX 线程简介

POSIX 提供以下函数:

        pthread_create():创建一个线程
	pthread_exit():终止当前线程
	pthread_cancel():中断另外一个线程的运行
	pthread_join():阻塞当前的线程,直到另外一个线程运行结束
	pthread_attr_init():初始化线程的属性
	pthread_attr_setdetachstate():设置脱离状态的属性(决定这个线程在终止时是否可以被结合)
	pthread_attr_getdetachstate():获取脱离状态的属性
	pthread_attr_destroy():删除线程的属性
	pthread_kill():向线程发送一个信号

1.1线程创建:


#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

//返回:成功为0 失败为错误码

我们注意第3个参数是一个函数指针,它返回void * 参数也是void *,这就是所谓线程(第三个参数里理解为线程要做的工作),第四个参数则是线程的参数.

POSIX的thread默认是joinable,需要手工调用pthread_join函数来回收,也可以调用pthread_detach将其变为detachable,此时不需要手工回收线程。

下面介绍Thread的封装。

通常情况下,我们可能会这样写代码:

void *mythread(void *arg)
{
    //tasks...
}

int main()
{
    pthread_t tid1;
    pthread_create(&tid1,NULL,mythread,NULL);
    //main thread
    pthread_join(tid1, NULL);
}

这里直接用函数名指代回调函数,也可以加上取地址符&

1.2线程回收:
首先得知道线程的两个状态:

Joinable 
Detached

简单理解,如果一个线程是joinable的状态,那么这样的线程,就必须使用pthread_join来回收,否则程序结束时,线程所占用的资源不会释放,就会造成内存泄漏。我们通常在主进程中会阻塞调用pthread_join来等待我们的线程结束。

如果是Detached状态的线程,那么在线程结束后,资源会自动释放,POSIX pthread线程库中,提供下面函数来让线程进入Detached状态:

#include <pthread.h>
int pthread_detach(pthread_t thread);

 

在我们的设计中,考虑pthread_joinpthread_detach的特性,分别对成员函数join和析构函数进程封装。

线程创建时默认情况情况下是Joinable状态。
 

1.3示例:

通常只需注意线程的创建与回收即可,例如: 

#include<iostream>  
#include<pthread.h>  
#include <unistd.h>
using namespace std;  

void * child1(void * arg)  
{  
    pthread_t tid =pthread_self();  
    cout<<"thread "<<(unsigned int )tid<<" enter"<<endl;  
    //tasks
    cout<<"thread "<<tid<<" return"<<endl;  
}  
int main(void)  
{  
    pthread_t tid1;  
    cout<<"hello main thread"<<endl;  
    pthread_create(&tid1,NULL,child1,NULL);  
    pthread_join(tid1,NULL);
    cout<<"main thread exit"<<endl;  
    return 0;  

}  

2.线程封装

封装为一个C++ 类需要一些技巧:

1.Thread是没有拷贝构造和赋值语义的,因此也需要继承自boost::noncopyable 
2.关于pthread_create的第三个参数(返回void*参数为void*的函数指针),因为成员函数会隐式包含this指针作为参数,所以加以static修饰。 
3.对于pthread_create的第四个参数(void *arg作为线程参数),传入this指针方便线程调用成员函数。 
4.使用join()成员函数封装pthread_join(),析构函数使用pthread_detach()处理。 
5.设置run()成员函数为纯虚函数,子类重写run()方法。
 

代码如下

#pragma once
#include <sys/types.h>
#include <semaphore.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include "CCondition.h"
#include "CThreadMutex.h"

using namespace std;

/* Thread state.  */
typedef enum 
{
	THREAD_IDLE,
#define THREAD_IDLE	THREAD_IDLE
	THREAD_RUNNING,
#define THREAD_RUNNING	THREAD_RUNNING
	THREAD_WAKEUP,
#define THREAD_WAKEUP	THREAD_WAKEUP
	THREAD_END_NORMAL,
#define THREAD_END_NORMAL	THREAD_END_NORMAL
	THREAD_END_ERROR,
#define THREAD_END_ERROR	THREAD_END_ERROR
}ThreadState;
#define  NOTDETACH	false
#define  DETACH		true

class CThread : public boost::noncopyable
{
public:
	//线程的构造函数
	CThread()
	{
		this->m_ThreadID = NULL;//初始化线程ID
		this->m_ThreadState = THREAD_IDLE;//初始化线程撞他
	}
	//线程想析构函数
	virtual ~CThread()
	{
		if(this->m_ThreadState==THREAD_RUNNING)
		{
			CHECK(!pthread_detach(this->m_ThreadID));//如果线程正在运行,则让其状态为detached
		}
	}

	//线程的任务函数(纯虚函数)
	virtual void Run(void) = 0;
	
	//线程入口(实际创建线程的地方)
	void Start(void)	//入口
	{	
	
	CHECK(!pthread_create(&this->m_ThreadID, NULL, CThread::ThreadFunction, this));
	} 

	//获得线程ID
	int GetThreadID(void)
	{
		return this->m_ThreadID;
	}

	//让其状态为detached
	bool Detach(void)
	{
		CHECK(!pthread_detach(this->m_ThreadID));
	}
	//回收线程
	bool Join(void)
	{	
		assert(this->m_ThreadState==THREAD_RUNNING);
	 	CHECK(!pthread_join(this->m_ThreadID, NULL));//回收线程
	}


protected:
	static void* ThreadFunction(void* arg)
	{
		CThread* pt = static_cast<CThread*> (arg);
		pt->Run();
		return NULL;
	}
private:
	pthread_t m_ThreadID;
	ThreadState m_ThreadState;//线程状态 
};

这里对成员函数进行定义,run()由于是纯虚函数,我们放到子类中重写:

这里需要说明的是:

首先,为了获得最干净的语义,Thread应该是不可复制的,所以需要继承NonCopyable。

其次,为了调用pthread_create创建线程,我们往里面注册的不能是一个成员函数,因为成员函数含有一个隐式参数(this),导致函数的指针类型并不是void *(*start_routine) (void *),所以我们采用了static函数

static函数无法访问某一对象的成员,所以我们在调用pthread_create时,将this指针作为回调函数的参数。(即上文锁提到的第四个参数)

这个Thread不提供detach函数,因为我们在析构函数中做了如下的处理,如果Thread对象析构,线程还在运行,那么需要将Thread设置为detach状态

 

大部分逻辑都是固定的,用户只需要改变run里面的代码即可,于是我们将run设置为纯虚函数,让用户继承Thread类

所以析构函数为virtual

原创文章 11 获赞 9 访问量 306

猜你喜欢

转载自blog.csdn.net/qq_41868108/article/details/105781737