今天寫了一個類,類中用到了創建線程函數:_beginthreadex() (windows下的創建線程函數,這其實是一個C標準的線程函數,但跟windows聯繫緊密,只要是爲解決Create_Thread()函數創建的線程如果調用了C函數庫,就會發生內存泄露,所以這是個安全的函數,建議用這個。) 以下是一個成員函數的某段代碼:
HANDLE hth;
unsigned uiThread1ID;
hth = (HANDLE)_beginthreadex( NULL, // security
0, // stack size
NetRawThreadProc,
this, // arg list
0, // so we can later call ResumeThread()
&uiThread1ID );
CloseHandle(hth);
功能就是創建一個線程,然後線程去執行NetRawThreadProc(),這個NetRawThreadProc 也是一個類成員函數,它的原型爲:
void* NetWorkThread::NetRawThreadProc (void* pParam)
{
//blablabla~~~
}
然後編譯一下,傻眼了,說該成員函數與回調類型不匹配噢,MLGB!
我從頭到尾都是參照標準的方法,來調用回調函數啊(因爲還不知道this指針這個鬼~)
所以我的解決思路是:
1. 回調類型不匹配,我就先check 我的NetRawThreadProc() 的返回值類型、參數類型,發現沒有錯;
2.是否_beginthreadex()的第三個參數給錯了,然後查到這第三個參數,有兩種使用方式,一種是在調用的函數名前面加 & ,一種是不加:
函數名,本來就是函數地址,這時候再加個 & 也是一樣,地址不會發生改變,所以還是能夠調用;
3.可能是NetWorkThread::有問題:
try了一下,把NetWrokThread:: 去掉後再來調用,就一切正常。
找到問題了,成員函數作爲回調函數,會出問題。
解決方法一:
用普通函數來做中間層:
void* func(void*)
普通函數爲上述格式,然後利用回調函數的特性,傳遞 this 指針到第四個參數,然後func()就可以使用this指針來調用類成員函數了,相當折中。
但我不喜歡這個方式,因爲這樣封裝性就破壞了。
這個時候有第二種解決方法:使用static 成員函數作爲回調函數,然後在static中調用成員函數;
void* NetWorkThread::NetRawThreadProc (void* pParam)
{
NetThreadProc();
}
此時編譯報錯,提示NetThreadProc也需要是靜態成員;我在這裏調用成員函數,而不是直接用static成員函數來進行操作,是因爲static要操作的類成員變量必須是static的,這樣我就需要把成員變量該爲static了。所以必須要用某種方法來調用成員函數。
我們可以利用回調函數傳遞this指針,迂迴解決這個問題:
void* NetWorkThread::NetRawThreadProc (void* pParam)
{
NetWorkThread *netObject = (NetWorkThread*)pParam;
netObject->NetThreadProc();
}
這樣,類成員函數就能正確調用了,整體格式爲:
HANDLE hth;
unsigned uiThread1ID;
hth = (HANDLE)_beginthreadex( NULL, // security
0, // stack size
NetRawThreadProc,
this, // arg list
0, // so we can later call ResumeThread()
&uiThread1ID );
CloseHandle(hth);
void* NetWorkThread::NetRawThreadProc (void* pParam)
{
NetWorkThread *netObject = (NetWorkThread*)pParam;
netObject->NetThreadProc();
}
void NetWorkThread::NetThreadProc(){//blablalbla}