windows下c++类成员方法作为线程函数
类成员方法是一个比较特殊的函数,它在编译时会被转化成普通函数,比如有TMyClass类:
class TMyClass
{
void Func();
};
这个TMyClass::Func最终会转化成 void Func(TMyClass *this); 也就是说在原第一个参数前插入指向对象本身的this指针。
我们可以利用这个特性写一个非静态类成员方法来直接作为线程回调函数,先看_beginthread函数的定义:
unsigned long _RTLENTRY _EXPFUNC _beginthread (void (_USERENTRY *__start)(void *),unsigned __stksize, void *__arg);
其中的第一个参数就是作为线程执行主体的回调函数。它的原型是:void Func(void *),这个void*参数是作为自定义数据传入的。对比一下上面所说的TMyClass::Func的最终形式,它正好可以符合这里的要求。
现在做个实验:
#include
#include
class TMyClass
{
int m_nCount;
int m_nId;
public:
TMyClass(int nId,int nCount)
:m_nId(nId),m_nCount(nCount)
{ }
void _USERENTRY ThreadProc() // 类成员方法
{
for(int i=0; i<m_nCount; i++) // 根据m_nCount成员打印一排数字
{
printf("Class%d : %dn",m_nId,i);
}
}
};
int main(int argc, char* argv[])
{
// 联合类,用于转换类成员方法指针到普通函数指针(试过编译器不允许在这两种函数之间强制转换),不知道有没有更好的方法。
union {
void (_USERENTRY *ThreadProc)(void *);
void (_USERENTRY TMyClass::*MemberProc)();
} Proc; // 尽管联合里的两种函数类型现在看起来有很大不同,但它们的最终形式是相同的。
TMyClass MyClass1(1,10),MyClass2(2,5); // 产生两个TMyClass对象
Proc.MemberProc = &TMyClass::ThreadProc; // 转换,Proc.ThreadProc就是对应的普通函数指针了
_beginthread(Proc.ThreadProc,4096,&MyClass1); // 开始线程,这里的Proc.ThreadProc实际上是TMyClass::ThreadProc, 它要的this指针是我们给的&MyClass1。
_beginthread(Proc.ThreadProc,4096,&MyClass2);
system("pause");
return 0;
}
其实不止线程回调函数,其实只要是形如Func(void*,...)的回调函数都可以用这种方法直接使用类成员方法。(前提是第一个void*是自定义数据,也就是说它不能有其它功能)。
Linux下类成员函数作为Pthread库线程函数
方法一:使用静态成员函数
第一个解决方法是使回调成员函数为静态。因为静态成员函数不带隐含式参数“this”。因此,可以将其参数中的地址当作是普通函数的指针来使用。如
果要从静态成员函数中访问对象的数据成员,显式传入对象的地址即可。例如:
class Hack
{
private:
int x;
public:
int get_x();
static void func(Hack * pthis); // 静态成员函数
void func2(); // 非静态成员函数
};
void Hack::func(Hack * pthis)
{
int y = pthis->get_x(); // 访问对象的数据成员
}
这个方法在大多数情形下都能行得通,但有时候成员函数不能声明为静态,也就是说成员函数是虚函数或者正在使用不能修改的第三方类。遇到这种情况
时,用方法一解决问题就比较难了。
方法二:处理非静态成员函数
假设需要在单独的线程中调用类Hack的非静态成员函数func2()。不用直接传递成员函数的地址到thr_create(),声明一个带 void* 参数的普通函数
intermediary(void*),然后调用它:
void *intermediary(void*);
接着创建一个结构,结构定义如下:
struct A
{
Hack * p; //类对象指针
void (Hack::*pmf)(); // 成员函数指针
};
创建一个结构实例,用希望的对象地址和成员函数地址填充结构:
A a; // 结构实例
Hack h; // 创建对象
//填充结构
a.p = & h;
a.pmf = &Hack::func2; // 取成员函数地址
现在回过头来实现intermediary()函数:
void *intermediary(void* ptr)
{
A* pa=static_cast < A* > (ptr); // 强制转换 p 为 A*
Hack* ph=pa->p; // 从A中析取Hack对象地址
void (Hack::*pmf)()=pa->pmf; // 析取 ptr 到成员函数
(ph->*pmf)(); // 调用成员函数
}
最后将intermediary()的地址传递到thr_create():
pthread_create (&ptid, NULL, intermediary, (void *)&a );
pthread_create()调用函数intermediary()并将A的地址传递给它。intermediary()再从其指针参数中展开结构A并调用希望的成员函数。这种间接方式的
处理可以安全地在单独线程中启动成员函数,即便是线程库不支持成员函数。如果需要调用不同类的不同成员函数,可以将结构A转换成类模板,将函数
intermediary()转换成函数模板。从而编译器便会自动产生大多数样板文件代码。