通常在界面編程中,我們一方面要提供一個(gè)良好的界面環(huán)境給用戶,同時(shí)后臺(tái)還要做相關(guān)的應(yīng)用處理,比如對(duì)數(shù)據(jù)庫的查詢、更新;復(fù)雜的計(jì)算處理。而這往往是很耗時(shí)的,如果界面更新和后臺(tái)處理在同一個(gè)主線程中,那么界面的更新將受到后臺(tái)處理程序的影響,特別是當(dāng)后臺(tái)程序比較耗時(shí)時(shí),界面往往得不到及時(shí)更新,會(huì)給用戶造成程序死掉的假象。對(duì)于這種情況,我們通常會(huì)采用多線程編程的方式來實(shí)現(xiàn),即響應(yīng)用戶的操作在一個(gè)線程,而用戶后臺(tái)處理在另外一個(gè)線程。
在有些時(shí)候,我們要新開一個(gè)線程,在這個(gè)線程里面要訪問到類的成員函數(shù)和成員變量,但是CreateThread函數(shù)要求提供的線程函數(shù)必須是靜態(tài)成員函數(shù)或者全局函數(shù),這樣在線程里面是不能夠訪問到類的成員變量的。這就是一個(gè)比較麻煩的矛盾。
完整的方案如下:
#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);
}
一種解決方案:將對(duì)象的this指針作為參數(shù)傳遞給線程函數(shù),然后在線程函數(shù)里面將其轉(zhuǎn)換為相應(yīng)的類對(duì)象指針,在里面調(diào)用指針進(jìn)行相應(yīng)的操作就OK了。
相應(yīng)的例子如下:
1.在這里創(chuàng)建新的線程,將this指針傳遞給線程函數(shù) MyFunction
m_hThread=CreateThread(NULL,NULL,CRealPlayer::MyFunction, (LPVOID)this,NULL,NULL);
2.//線程函數(shù),在線程里面實(shí)現(xiàn)將CRealPlayer的member2和member3成員
相加,結(jié)果放到成員member1中。
這個(gè)線程函數(shù)是 static的成員函數(shù)。
DWORD WINAPI CRealPlayer::MyFunction(LPVOID lpParameter)
{
CRealPlayer * lp=(CRealPlayer *)lpParameter;
lp->member1=lp->member2+lp->member3;
}
多線程在很多地方都有講述,但大都作為全局函數(shù),且不能對(duì)類的成員進(jìn)行訪問,本文講述如何將一個(gè)線程定義在一個(gè)類中以及如何通過這個(gè)線程實(shí)現(xiàn)對(duì)類中的成員進(jìn)行訪問。
多線程創(chuàng)建線程時(shí),線程函數(shù)如果要設(shè)置成類的成員函數(shù),則必須是靜態(tài)成員函數(shù),在此函數(shù)種不能使用非靜態(tài)成員變量,如果要使用非靜態(tài)成員變量,則一種比較適合線程的方法是:建立線程的時(shí)候把this作為CreateThread的一個(gè)參數(shù)(即第4個(gè)參數(shù),就是那個(gè)LPVOID型的),然后線程里就對(duì)應(yīng)pParam,例如:
在類中,多線程函數(shù)必須定義為靜態(tài)函數(shù)
Class CMutilThread: Public CDialog
{
………
private:
HANDLE
hThread; DWORD
idThread; UINT
couter; protected:
public:
static
DWORD WINAPI ThreadPro(LPVOID lpParam); };
多線程函數(shù)的實(shí)現(xiàn)(本例中每次線程被調(diào)用的時(shí)候均對(duì)計(jì)數(shù)器加1)
DWORD WINAPI CMutilThread(LPVOID)
{
CMutilThread
*pDlg = (CMutilThread*)lpParam;//定義指向本類的指針,相當(dāng)于this pDlg->couter++;
//計(jì)數(shù)器加1 }
多線程函數(shù)的創(chuàng)建
void CMutiThread::On
Start() {
hThread = CreateThread (NULL, 0,THreadPro,this,0,idThread);
}
多線程的終止
void CMutiThread::On
End() {
TerminalThread(hThread,0);
}
//*******************************************************************************************************************************
C++類成員函數(shù)直接作為線程回調(diào)函數(shù)
以前寫線程時(shí)要么老老實(shí)實(shí)照著聲明寫,要么使用C++類的靜態(tài)成員函數(shù)來作為回調(diào)函數(shù),經(jīng)常會(huì)因?yàn)榫€程代碼而破壞封裝.之前雖然知道類成員函數(shù)的展開形式,但從沒想過利用過它,昨天看深入ATL時(shí)無意中學(xué)會(huì)了這一招:)
類成員方法是一個(gè)比較特殊的函數(shù),它在編譯時(shí)會(huì)被轉(zhuǎn)化成普通函數(shù),比如有TMyClass類:
class TMyClass{
void Func();
};
這個(gè)TMyClass::Func最終會(huì)轉(zhuǎn)化成 void Func(TMyClass *this); 也就是說在原第一個(gè)參數(shù)前插入指向?qū)ο蟊旧淼膖his指針。
我們可以利用這個(gè)特性寫一個(gè)非靜態(tài)類成員方法來直接作為線程回調(diào)函數(shù),先看_beginthread函數(shù)的定義:
unsigned long _RTLENTRY _EXPFUNC _beginthread (void (_USERENTRY *__start)(void *),unsigned __stksize, void *__arg);
其中的第一個(gè)參數(shù)就是作為線程執(zhí)行主體的回調(diào)函數(shù)。它的原型是:void Func(void *),這個(gè)void*參數(shù)是作為自定義數(shù)據(jù)傳入的。對(duì)比一下上面所說的TMyClass::Func的最終形式,它正好可以符合這里的要求。
現(xiàn)在做個(gè)實(shí)驗(yàn):
#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++) // 根據(jù)m_nCount成員打印一排數(shù)字
{
printf("Class%d : %d\n",m_nId,i);
}
}
};
int main(int argc, char* argv[])
{
union { // 聯(lián)合類,用于轉(zhuǎn)換類成員方法指針到普通函數(shù)指針(試過編譯器不允許在這兩種函數(shù)之間強(qiáng)制轉(zhuǎn)換),不知道有沒有更好的方法。
void (_USERENTRY *ThreadProc)(void *);
void (_USERENTRY TMyClass::*MemberProc)();
} Proc; // 盡管聯(lián)合里的兩種函數(shù)類型現(xiàn)在看起來有很大不同,但它們的最終形式是相同的。
TMyClass MyClass1(1,10),MyClass2(2,5); // 產(chǎn)生兩個(gè)TMyClass對(duì)象
Proc.MemberProc = &TMyClass::ThreadProc; // 轉(zhuǎn)換,Proc.ThreadProc就是對(duì)應(yīng)的普通函數(shù)指針了
_beginthread(Proc.ThreadProc,4096,&MyClass1); // 開始線程,這里的Proc.ThreadProc實(shí)際上是TMyClass::ThreadProc, 它要的this指針是我們給的&MyClass1。
_beginthread(Proc.ThreadProc,4096,&MyClass2);
system("pause");
return 0;
}
運(yùn)行!神奇吧?:-)
其實(shí)不止線程回調(diào)函數(shù),其實(shí)只要是形如Func(void*,...)的回調(diào)函數(shù)都可以用這種方法直接使用類成員方法。(前提是第一個(gè)void*是自定義數(shù)據(jù),也就是說它不能有其它功能)。
聯(lián)系客服