通常在界面编程中,我们一方面要提供一个良好的界面环境给用户,同时后台还要做相关的应用处理,比如对数据库的查询、更新;复杂的计算处理。而这往往是很耗时的,如果界面更新和后台处理在同一个主线程中,那么界面的更新将受到后台处理程序的影响,特别是当后台程序比较耗时时,界面往往得不到及时更新,会给用户造成程序死掉的假象。对于这种情况,我们通常会采用多线程编程的方式来实现,即响应用户的操作在一个线程,而用户后台处理在另外一个线程。
在有些时候,我们要新开一个线程,在这个线程里面要访问到类的成员函数和成员变量,但是CreateThread函数要求提供的线程函数必须是静态成员函数或者全局函数,这样在线程里面是不能够访问到类的成员变量的。这就是一个比较麻烦的矛盾。
完整的方案如下:
#include<windows.h>
#include<stdio.h>
#include<process.h>
class test
{
public:
};
int main()
{
或者m_hThread=CreateThread(NULL,NULL,CRealPlayer::MyFunction, (LPVOID)this,NULL,NULL);
}
一种解决方案:将对象的this指针作为参数传递给线程函数,然后在线程函数里面将其转换为相应的类对象指针,在里面调用指针进行相应的操作就OK了。
相应的例子如下:
1.在这里创建新的线程,将this指针传递给线程函数 MyFunction
m_hThread=CreateThread(NULL,NULL,CRealPlayer::MyFunction, (LPVOID)this,NULL,NULL);
2.//线程函数,在线程里面实现将CRealPlayer的member2和member3成员
相加,结果放到成员member1中。
这个线程函数是 static的成员函数。
DWORD WINAPI CRealPlayer::MyFunction(LPVOID lpParameter)
{
CRealPlayer * lp=(CRealPlayer *)lpParameter;
lp->member1=lp->member2+lp->member3;
}
多线程在很多地方都有讲述,但大都作为全局函数,且不能对类的成员进行访问,本文讲述如何将一个线程定义在一个类中以及如何通过这个线程实现对类中的成员进行访问。
多线程创建线程时,线程函数如果要设置成类的成员函数,则必须是静态成员函数,在此函数种不能使用非静态成员变量,如果要使用非静态成员变量,则一种比较适合线程的方法是:建立线程的时候把this作为CreateThread的一个参数(即第4个参数,就是那个LPVOID型的),然后线程里就对应pParam,例如:
在类中,多线程函数必须定义为静态函数
Class CMutilThread: Public CDialog
{
………
private:
HANDLE
hThread; DWORD
idThread; UINT
couter; protected:
public:
static
DWORD WINAPI ThreadPro(LPVOID lpParam); };
- 多线程的实现
多线程函数的实现(本例中每次线程被调用的时候均对计数器加1)
DWORD WINAPI CMutilThread(LPVOID)
{
CMutilThread
*pDlg = (CMutilThread*)lpParam;//定义指向本类的指针,相当于this pDlg->couter++;
//计数器加1 }
多线程函数的创建
void CMutiThread::On
Start() {
hThread = CreateThread (NULL, 0,THreadPro,this,0,idThread);
}
多线程的终止
void CMutiThread::On
End() {
TerminalThread(hThread,0);
}
//*******************************************************************************************************************************
C++类成员函数直接作为线程回调函数
以前写线程时要么老老实实照着声明写,要么使用C++类的静态成员函数来作为回调函数,经常会因为线程代码而破坏封装.之前虽然知道类成员函数的展开形式,但从没想过利用过它,昨天看深入ATL时无意中学会了这一招:)
类成员方法是一个比较特殊的函数,它在编译时会被转化成普通函数,比如有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 <stdio.h>
#include <process.h>
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 : %d\n",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*是自定义数据,也就是说它不能有其它功能)。