首先我們需要搞清楚一個問題,爲什麼我們要用Thunk技術?Thunk技術可以解決神馬難題?
在探尋上面的問題之前先講述一下線程從面向過程到面相對象編程所面臨的問題和解決的方法,因爲Thunk也是爲了解決這些問題而產生的
我們知道用Window的API來開發程序的話實際上是用的C語言的面向過程的方式來開發的。
而在我們使用C++開發程序的時候通常都是用的OOP(面向對象編程)的方式來編寫的。
例如在C語言裏面創建線程時是直接用_beginthread()一條函數就 可以的了,而在c++裏面我們通常想要封裝成一個線程類。
來看看線程是怎麼轉變的?先看下_beginthread()的聲名
uintptr_t _beginthread( // NATIVE CODE
void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist
);
_beginthread()的第一個參數說明了我們要創建線程就需要定義一個像下面這樣格式的函數
void ThreadFunc(void* pVoid);
在C的面相過程的編程方式中實際上這個函數是一個裸露在外的全局函數,如果我們要將它封裝到類裏面怎麼做呢?我們最直接的想法是這樣做
class MyClass
{
public:
void StartThread()
{
_beginthread(ThreadFunc, NULL, NULL);
}
...
void ThreadFunc(void* pVoid);
...
}
但是實際上這樣會編譯錯誤的,因爲ThreadFunc作爲成員函數會被編譯器編譯爲這樣的格式,導致在編譯的時候參數不一致
void MyClass::ThreadFunc(this, void* pVoid)
成員函數會被加上this指針,如果不知道爲什麼就百度一下編譯器編譯成員函數的方法吧。。。
那麼是不是ThreadFunc沒辦法放在類裏面了呢?有沒有解決辦法呢?有的,只要把ThreadFunc聲名爲類的靜態成員函數就可以了,因爲被聲名爲static的函數是不會被在參數列表裏面加上this指針的。於是修改如下
class MyClass
{
public:
void StartThread()
{
_beginthread(ThreadFunc, NULL, NULL);
}
...
static void ThreadFunc(void* pVoid);
...
}
但是這樣的話問題又來了,我們知道靜態成員函數實際上是不屬於類的任何對象的,也就是靜態函數無法訪問類對象的成員變量,因爲它沒有this指針。
這是個非常糟糕的情況啊,因爲我們的線程通常都要訪問類對象的成員變量的(總要存取某些值之類的吧),那麼我們是不是可以嘗試把this指針傳遞給靜態函數ThreadFunc呢?這樣我們就能用這個this指針做普通成員函數做的事情了。
我們注意到ThreadFunc(void* pVoid)是有個void類型的指針的參數傳入的,從字面意思是可以傳入任意的指針的。那麼我們就從這個入手,我們把this指針用這個pVoid參數傳入進去。再在ThreadFunc內部轉換一下指針類型就大功告成了。
再往上看一下_beginthread()的聲名,發現最後一個參數實際上就是會傳遞給ThreadFunc的void參數的。我們這樣做就可以了
class MyClass
{
public:
void StartThread()
{
_beginthread(ThreadFunc, NULL, this);
}
...
void ThreadFunc(void* pVoid);
...
}
線程從面向過程到面向對象的轉變有些曲折,但是總歸是順利的利用pVoid這個預留的參數解決了