POSIX的thread默认是joinable,需要手工调用pthread_join函数来回收,也可以调用pthread_detach将其变为detachable,此时不需要手工回收线程。
封装时有三个注意事项:
- Thread应该是不可拷贝的,所以继承noncopyable
- 其次,为了调用pthread_create创建线程,我们往里面注册的不能是一个成员函数,因为成员函数含有一个隐式参数(this),导致函数的指针类型并不是void *(*start_routine) (void *),所以我们采用了static函数。
- static函数无法访问某一对象的成员,所以我们在调用pthread_create时,将this指针作为回调函数的参数。
方式一
封装
下面采用继承的方式进行封装,子类只需公有继承Thread类并覆写其run方法。
- 因为有虚函数run(),根据《Effctive C++》条款七,要把基类的析构函数声明为虚函数。
- run为纯虚函数,子类必须覆写此方法,否则无法创建对象。
(联想:将析构函数声明为纯虚函数会怎么样,见 https://www.cnblogs.com/chio/archive/2007/09/10/888260.html)
#ifndef _THREAD_H_
#define _THREAD_H_
#include "noncopyable.h"
#include <pthread.h>
#include <cstdio>
#include <cstdlib>
#define CHECK(exp)\
if(!exp) \
{ \
fprintf(stderr,"file:%s,line:%d Exp:[" #exp "] not return 0\n",__FILE__,__LINE__);abort();\
}
class Thread: public noncopyable{
public:
Thread();
virtual ~Thread();
void join();
virtual void run() = 0;
void start();
pthread_t getThreadId(){
return tid_;
}
private:
static void* func(void* arg);
private:
pthread_t tid_;
bool isrunning_;
};
#endif // _THREAD_H_
#include "Thread.h"
#include <assert.h>
Thread::Thread():tid_(0),isrunning_(false){
}
void Thread::start(){
CHECK(!pthread_create(&tid_,NULL,Thread::func,(void*)this));
isrunning_ = true;
}
void Thread::join(){
assert(isrunning_ == true);
CHECK(!pthread_join(tid_,NULL));
isrunning_ = false;
}
void* Thread::func(void* arg){
Thread* t = (Thread*)arg;
t->run();
return NULL;
}
Thread::~Thread(){
if(isrunning_){
CHECK(!pthread_detach(tid_));
}
}
使用
还是利用封装Condition时的例子:
https://blog.youkuaiyun.com/littleflypig/article/details/89210990
这次把线程换成我们自己的。
#include <iostream>
#include "MutexLock.h"
#include <pthread.h>
#include <cstdlib>
#include <unistd.h>
#include <sys/syscall.h>
#include "Condition.h"
#include "Thread.h"
using namespace std;
const int num = 10;
int count=0;
MutexLock mutex;
Condition cond(mutex);
pid_t gettid(){
return syscall(__NR_gettid);
}
class ConsumerThread : public Thread{
public:
void run(){
pid_t tid = gettid();
for(int i=0;i<num;++i){
MutexLockGuard lock(mutex);
while(count == 0){
cond.wait();
}
cout<<"consume "<<tid<<": "<<count<<endl;
count--;
}
}
};
class ProducerThread : public Thread{
public:
void run(){
pid_t tid = gettid();
for(int i=0;i<2*num;++i){
MutexLockGuard lock(mutex);
count++;
cout<<"produce "<<tid<<": "<<count<<endl;
cond.notify();
}
}
};
int main()
{
ConsumerThread c1,c2;
ProducerThread p;
c1.start();
c2.start();
p.start();
c1.join();
c2.join();
p.join();
cout<<count<<endl;
}
有几个点需要注意:
- 继承Thread类时要使用public继承,这样才能在main函数(用户代码)中调用基类部分的start,join等方法。
- Thread派生类可以不写析构,由编译器合成默认的。
参考:http://www.cnblogs.com/inevermore/p/4008572.html
方式二
利用std的function模板,需要注意的点是setCallback的参数必须要声明为const类型的引用,否则编译会报错。
#ifndef _THREAD2_H_
#define _THREAD2_H_
#include "noncopyable.h"
#include <pthread.h>
#include <cstdio>
#include <cstdlib>
#include <functional>
#define CHECK(exp)\
if(!exp) \
{ \
fprintf(stderr,"file:%s,line:%d Exp:[" #exp "] not return 0\n",__FILE__,__LINE__);abort();\
}
typedef std::function<void ()> funcCallback;
class Thread2: public noncopyable{
public:
Thread2();
~Thread2();
void join();
void start();
pthread_t getThreadId(){
return tid_;
}
void setCallback(const funcCallback& c){
callback_ = c;
}
private:
static void* func(void* arg);
private:
pthread_t tid_;
bool isrunning_;
funcCallback callback_;
};
#endif // _THREAD_H_
#include "Thread2.h"
#include <assert.h>
Thread2::Thread2():tid_(0),isrunning_(false){
}
void Thread2::start(){
CHECK(!pthread_create(&tid_,NULL,Thread2::func,(void*)this));
isrunning_ = true;
}
void Thread2::join(){
assert(isrunning_ == true);
CHECK(!pthread_join(tid_,NULL));
isrunning_ = false;
}
void* Thread2::func(void* arg){
Thread2* t = (Thread2*)arg;
t->callback_();
return NULL;
}
Thread2::~Thread2(){
if(isrunning_){
CHECK(!pthread_detach(tid_));
}
}
使用起来感觉比较方便,不用继承了
include <iostream>
#include "MutexLock.h"
#include <pthread.h>
#include <cstdlib>
#include <unistd.h>
#include <sys/syscall.h>
#include "Condition.h"
#include "Thread2.h"
using namespace std;
const int num = 10;
int count=0;
MutexLock mutex;
Condition cond(mutex);
pid_t gettid(){
return syscall(__NR_gettid);
}
void consume(){
pid_t tid = gettid();
for(int i=0;i<num;++i){
MutexLockGuard lock(mutex);
while(count == 0){
cond.wait();
}
cout<<"consume "<<tid<<": "<<count<<endl;
count--;
}
}
void produce(){
pid_t tid = gettid();
for(int i=0;i<2*num;++i){
MutexLockGuard lock(mutex);
count++;
cout<<"produce "<<tid<<": "<<count<<endl;
cond.notify();
}
}
int main()
{
Thread2 c1,c2,p;
c1.setCallback(consume);
c2.setCallback(consume);
p.setCallback(produce);
c1.start();
c2.start();
p.start();
c1.join();
c2.join();
p.join();
cout<<count<<endl;
}
而且想要传参数给线程任务函数也是可以的,用bind将参数绑定即可,如下:
void consume(int arg){
pid_t tid = gettid();
for(int i=0;i<num;++i){
MutexLockGuard lock(mutex);
while(count == 0){
cond.wait();
}
cout<<"consume "<<tid<<": "<<count<<endl;
count--;
}
}
int main()
{
Thread2 c1,c2,p;
c1.setCallback(bind(consume,1)); //bind参数
c2.setCallback(bind(consume,2));
p.setCallback(produce);
c1.start();
c2.start();
p.start();
c1.join();
c2.join();
p.join();
cout<<count<<endl;
}