類成員函式作為pthread_create函式引數
近日需要將執行緒池封裝成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);//啟動執行緒執行例程
----------------------------------------------------------------------------------------------------------------------
在網上搜索一下還有其他的解決方案,摘錄如下,為了以示尊重標明文章來源,感謝原文作者。
方案二:
將執行緒啟動函式宣告為模板函式。
摘錄自:http://hi.baidu.com/angelevil2006/item/e1806ec30574ff11515058d1
[cpp] view plain copy- template <typename TYPE, void (TYPE::*_RunThread)() >
- void* _thread_t(void* param)//執行緒啟動函式,宣告為模板函式
- {
- TYPE* This = (TYPE*)param;
- This->_RunThread();
- return NULL;
- }
- class MyClass
- {
- public:
- MyClass();
- void _RunThread();
- private:
- pthread_t tid;
- };
- void MyClass::_RunThread()
- {
- this->DoSomeThing();
- }
- MyClass::MyClass()
- {
- pthread_create(&tid, NULL, _thread_t<MyClass, &MyClass::_RunThread>, this);
- }
- //函式模版不單可以替換型別本身,還能替換型別的成員函式。
- //注意: 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 <typename TYPE, void (TYPE::*Run)() >
void * thread_rounter(void * param)
{
TYPE *p=(TYPE*)param;
p->Run();
return NULL;
}
exec_task函式中呼叫:
pthread_create(&tid,NULL,thread_rounter<Threadpool,&Threadpool::Run>,(void *)this);
總結:
解決這個問題的關鍵在於想方設法使啟動函式指標滿足void*(*)(void *)型別。
將啟動函式改寫成static成員函式適用於可以修改類的原始碼的情況。
而將啟動函式寫成模板函式還可以適用於沒有類的原始碼的情況,自己寫一個類,公共繼承自原類,新增啟動函式為模板函式即可。