一直感觉Java的framework是学习OO设计非常优秀的范例。在Java中有对Thread非常方便的封装,重用起来十分方便,于是自己也想用C++对posix的pthread接口做一个类似的封装。
事情没有想象那么简单,主要的问题出现在C和C++之间函数指针的转换上:
来看下面一个类的定义:
typedef void*(*pFunc)(void*);
class Thread
{
public:
static void* run(void*)
{
cout<<"fn"<<endl;
}
void* runMember(void*)
{
cout<<"fn"<<endl;
}
virtual void* runVirtual(void*)
{
cout<<"fn"<<endl;
}
};
{
public:
static void* run(void*)
{
cout<<"fn"<<endl;
}
void* runMember(void*)
{
cout<<"fn"<<endl;
}
virtual void* runVirtual(void*)
{
cout<<"fn"<<endl;
}
};
void pthread_create(pFunc){...}
这里class Thread中有三个函数,一个static的,一个member,一个虚函数。请问,三个函数中哪一个可以作为参数传给pthead_create(pFunc)?
C++语法定义不能强制类型转换的语法,一般都是编译器无法实现的语法。这里static void* run(void*)可以强制转换为C指针,而后两者不行。
原因是对于runMember成员函数,编译器在编译时会将函数的第一个参数默认的分配给this指针。故C++与C函数指针实际上类型不匹配,编译器当然也无法把第一个this指针给你去了,否则函数里如果要调成员就更麻烦了。
而对于runVirtual(void*),一来有同样的函数参数不匹配问题,二来对于含有virtual function的class,C++编译器会自动在类的内存空间内生成虚函数表,同时cpp编译器采取的标识虚函数的方式是为每一个虚函数保存一个id,调用虚函数通过虚函数表base+id的方式得到真实函数入口。故这里的runVirtual根据不同的编译实现,或许是虚函数表base值,或许是虚函数id。
所以对于一个Thread类,可以这样实现:
(
参考jdk的设计,thread可以通过两种方式创建,一是传函数指针(runnable接口)的方式直接创建THread对象,二是通过继承并扩展thread中run接口的方式创建thread。
)
//省略了错误处理的code
typedef void*(*PTHREADFUNC)(void*)
class Thread
{
public:
Thread(PTHREADFUNC pTFunc):m_tid(0)
{
m_pthis = NULL;
pthead_create(&m_tid,NULL,pTFunc,NULL);
pthread_detach(m_tid);
}
Thread():m_tid(0)
{
m_pthis = this;
pthead_create(&m_tid,NULL,&Thread::ThreadRun,NULL);
pthread_detach(m_tid);
}
static void* ThreadRun(void*)
{
if(NULL != m_pthis)
{
m_pthis->run();
}
}
virtual void run()=0;
private:
staitc Thread *m_pthis;
pthread_t m_tid;
};
Thread * Thread::m_pthis = NULL;
这里通过一个adapter function : ThreadRun ,充当了C与C+代码中的Adapter。
用户类只需要扩展Thread类实现run()这个virtual方法,并创建类的实例就能实际线程。
当然上面这个Thread类还有很多问题:如1.不是线程安全的。因为用了一个static变量m_pthis保存this指针,所以需要使用者在创建自定义对象的前后在应用层中加锁保护。
2.并没有实现Java中那样的Runnable接口:像Java中那样通过对象基类(Interface)的方式传递回调函数是一种非常优雅的方法。要实现Runnable接口,可以在Runnable类中做和上面Thread类似的方法,用adapter function实现C对Cpp虚函数的调用。
再贴一个加锁版本的部分代码:
typedef void*(*pTFUNC)(void*);
class Thread
{
public:
Thread();
Thread(pTFUNC runnable);
virtual void run();
static void* runGlobal(void*);
static int detach(pthread_t tid);
static int join(pthread_t tid,void **status);
static void sleep(unsigned long);
static void nsleep(unsigned long,unsigned long);
static pthread_t self_id();
pthread_t getThreadID();
int getErrorCode();
protected:
void start();
pTFUNC m_pRunnable;
pthread_t m_tid;
bool m_avaliable;
int m_errorcode;
static Thread* m_pthis;
static pthread_mutex_t m_mutex;
};
Thread* Thread::m_pthis=NULL;
pthread_mutex_t Thread::m_mutex=PTHREAD_MUTEX_INITIALIZER;
Thread::Thread()
{
m_pRunnable = NULL;
m_tid = 0;
m_avaliable = false;
m_errorcode=0;
pthread_mutex_lock(&m_mutex);
m_pthis = this;
start();
}
Thread::Thread(pTFUNC pRunnable)
{
m_pRunnable = pRunnable;
m_tid=0;
m_avaliable = false;
m_errorcode=0;
m_pthis = this;
start();
}
void Thread::run()
{
return;
}
void* Thread::runGlobal(void *args)
{
Thread *pLocal = m_pthis;
pthread_mutex_unlock(&m_mutex);
if(NULL != pLocal )
{
pLocal->run();
}
return (void*)0;
}
void Thread::start()
{
if( NULL!=m_pRunnable )
{
m_errorcode = pthread_create(&m_tid,NULL,m_pRunnable,NULL);
}
else
{
m_errorcode = pthread_create(&m_tid,NULL,(&Thread::runGlobal),NULL);
}
if(0==m_errorcode)
{
detach(m_tid);
m_avaliable = true;
}
#ifdef _DEBUG
printf("tid:%u,errorcode:%u,selfid:%u/n",m_tid,m_errorcode,pthread_self());
#endif
}
再贴一个加锁版本的部分代码:
typedef void*(*pTFUNC)(void*);
class Thread
{
public:
Thread();
Thread(pTFUNC runnable);
virtual void run();
static void* runGlobal(void*);
static int detach(pthread_t tid);
static int join(pthread_t tid,void **status);
static void sleep(unsigned long);
static void nsleep(unsigned long,unsigned long);
static pthread_t self_id();
pthread_t getThreadID();
int getErrorCode();
protected:
void start();
pTFUNC m_pRunnable;
pthread_t m_tid;
bool m_avaliable;
int m_errorcode;
static Thread* m_pthis;
static pthread_mutex_t m_mutex;
};
Thread* Thread::m_pthis=NULL;
pthread_mutex_t Thread::m_mutex=PTHREAD_MUTEX_INITIALIZER;
Thread::Thread()
{
m_pRunnable = NULL;
m_tid = 0;
m_avaliable = false;
m_errorcode=0;
pthread_mutex_lock(&m_mutex);
m_pthis = this;
start();
}
Thread::Thread(pTFUNC pRunnable)
{
m_pRunnable = pRunnable;
m_tid=0;
m_avaliable = false;
m_errorcode=0;
m_pthis = this;
start();
}
void Thread::run()
{
return;
}
void* Thread::runGlobal(void *args)
{
Thread *pLocal = m_pthis;
pthread_mutex_unlock(&m_mutex);
if(NULL != pLocal )
{
pLocal->run();
}
return (void*)0;
}
void Thread::start()
{
if( NULL!=m_pRunnable )
{
m_errorcode = pthread_create(&m_tid,NULL,m_pRunnable,NULL);
}
else
{
m_errorcode = pthread_create(&m_tid,NULL,(&Thread::runGlobal),NULL);
}
if(0==m_errorcode)
{
detach(m_tid);
m_avaliable = true;
}
#ifdef _DEBUG
printf("tid:%u,errorcode:%u,selfid:%u/n",m_tid,m_errorcode,pthread_self());
#endif
}