c 學習筆記之基礎 類內宣告執行緒函式的呼叫

2022-02-03 09:02:29 字數 3509 閱讀 5875

近日需要將執行緒池封裝成c++類,類名為threadpool。在類的成員函式exec_task中呼叫pthread_create去啟動執行緒執行例程thread_rounter。編譯之後報錯如下:

spfs_threadpool.cpp: in member function 『int threadpool::exec_task(task*)』:

spfs_threadpool.cpp:174: error: argument of type 『void* (threadpool::)(void*)』 does not match 『void* (*)(void*)』

出現型別不匹配的問題。因為pthread_create需要的引數型別為void* (*)(void*),而thread_rounter作為類的成員函式時其型別是void* (threadpool::)(void*)的成員函式指標。我們知道類的成員函式在經過編譯器處理之後,會變成帶有this指標引數的全域性函式,所以型別注定是不會匹配的。但是如果將thread_rounter宣告為static型別,那麼編譯器會將static形式的函式,轉換成不帶this指標的全域性函式,所以其型別可以與pthread_create需要的引數型別相匹配。但是類的靜態成員函式無法訪問類的非靜態成員,不過這可以通過傳遞this指標解決這個問題。

綜上,我的這個問題可以這個樣子解決。

出問題之前的**:

void *thread_rounter(void *)//執行緒執行函式

//直接訪問類的成員

exec_task函式中呼叫:

pthread_create(&tid,null,thread_rounter,null);//啟動執行緒執行例程

修復這個問題的**:

static void *thread_rounter(void *tmp)/執行緒執行函式

threadpool *p=(threadpool *)tmp;

//通過p指標間接訪問類的非靜態成員

exec_task函式中呼叫:

pthread_create(&tid,null,thread_rounter,(void *)this);//啟動執行緒執行例程

在網上搜尋一下還有其他的解決方案,摘錄如下,為了以示尊重標明文章**,感謝原文作者。

方案二:

將執行緒啟動函式宣告為模板函式。

摘錄自:

[cpp]view plain

copy

template   

void* _thread_t(void* param)//執行緒啟動函式,宣告為模板函式  

class myclass  

;  void myclass::_runthread()  

myclass::myclass()  

//函式模版不單可以替換型別本身,還能替換型別的成員函式。  

//注意:      1、名稱只能是_runthread,不能在指定模版引數的時候修改;           

//  2、_runthread只能是public的,除非把_thread_t定義到myclass的內部。  

採取這個方案放入我的專案中:

出問題之前的**:

void *thread_rounter(void *)//執行緒執行函式

//直接訪問類的成員

exec_task函式中呼叫:

pthread_create(&tid,null,thread_rounter,null);//啟動執行緒執行例程

修復這個問題的**:

新增public成員函式:

void run()

//該函式替換上述thread_rounter的執行**

thread_rounter修改為全域性模板函式:

template 

void * thread_rounter(void * param)

type *p=(type*)param;

p->run();

return null;

exec_task函式中呼叫:

pthread_create(&tid,null,thread_rounter,(void *)this);

總結:解決這個問題的關鍵在於想方設法使啟動函式指標滿足void*(*)(void *)型別。

將啟動函式改寫成static成員函式適用於可以修改類的源**的情況。

而將啟動函式寫成模板函式還可以適用於沒有類的源**的情況,自己寫乙個類,公共繼承自原類,新增啟動函式為模板函式即可。

花了三個工作日把原來寫的一段通訊守護程序**從過程方法改到了 template class,對於 template 的使用和類的派生明白了不少道理。還有個很受啟發的一點,就是 c++ 中如何使用類的成員函式作為建立執行緒的開始函式。

pthread_create 是 posix 標準下建立執行緒的函式,函式原型是:

int pthread_create(pthread_t *thread, pthread_attr_t *attr, void*(*start_routine)(void *), void *arg);

在 c 中,這個函式使用很簡單,只要定義乙個引數和返回值均為 void * 型別的函式,使用函式名字作為引數即可。就算不完全符合,可以使用 (void *(*)(void *)) 將其強制轉換為符合型別檢查規格的函式指標。但是,類的非靜態成員函式隱含 this 指標作為第乙個引數,所以引數完全不可能轉化為 void * 型別,而 c++ 的型別檢查要比 c 嚴格許多。由於我原來寫的**是 c 風格的,自然不會出現型別不符的問題,現在將執行緒開始函式封裝到乙個模板類中,再建立執行緒的時候就不能滿足需要了。

在試了幾種轉換無效之後,從網上搜到一種方法:定義執行緒開始函式為類的靜態成員函式(static member function),這樣就不隱含 this 指標了,然後將 this 指標從 pthread_create 最後乙個引數傳給開始函式,在函式中將 void * 型別的 this 指標強制轉換為類指標。看來靜態成員函式還是有些妙用的。

***************====我叫分割線***************====

帶著我媽和我妹在北京城裡轉悠了幾天,累得不行。主要原因在我,沒有考慮到身體因素,連著玩消耗太大,再加上自己也沒車沒房,倒公交車和住賓館也要走很遠,費時間又費勁。我都快受不了了,別說我媽了。以後再出去旅遊,堅決不會再連著轉三天以上,要麼緊緊張張地玩兩天,要麼就花時間長點兒,走走歇歇。唉,誰讓咱是窮人呢,又有錢又有閒的日子還沒過上呢!

今天看了下積壓很久的部落格訂閱,同學裡開始寫和繼續寫的人越來越多了。覺得有些文章比較陰沉低迷,因為自己從大三開始心情就老是跌宕起伏,反而在面對這許多次的分別聚首時坦然一些。要不是周熹在散夥飯時候專門招我,也不會哭得那麼厲害。散了散了散了吧,沒有離別,怎麼會有重逢呢?

還是在 yourui 的部落格上看到小恪去新疆的訊息,要是當面看到他,肯定會玩笑說發配三千里伊犁充軍去了。這在邊關待了兩年之後,再回來學積分拓撲之類的數學還能看得下去嗎?都說是命運無常,旅途坎坷卻能看更美風景,只是不知道那關外還是不是大漠孤煙長河落日的大西北?

剛才去吃飯,走在晚間暖暖的懶洋洋的空氣中,忽然有點兒秋天的感覺。想起 7 年前爸爸送我到商丘一高上學的情景,日子可真快啊!老了,老了!

C 學習筆記23,類內函式過載

該博文僅用於交流學習。請慎用於不論什麼商業用途,本博主保留對該博文的一切權利。博主部落格 在乙個類內,最常見的就是建構函式的過載了.這裡我就不介紹了.先來看看常見的類內過載.可能這個是為了留給使用者很多其它選擇的餘地吧.同一時候要記住是能夠通過const過載的,即引數是const和非const也算是...

C 基礎學習筆記 類模板

一 類模板 1 類模板作用 語法 template typename t class 類名 解釋 include include using namespace std class person tyname name tyage age intmain 2 類模板與函式模板的區別 1.類模板無法使...

C 多執行緒基礎學習筆記(七)

一 std async和std future的用法 std async是乙個函式模板,std future是乙個類模板 std async std launch async,mythread 如果std async 的第乙個引數改成std lauch deferred,那麼執行緒不會被馬上執行,而是...