1. 程式人生 > >類成員函式作為pthread_create函式引數

類成員函式作為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  
  1. template <typename TYPE, void (TYPE::*_RunThread)() >  
  2. void* _thread_t(void* param)//執行緒啟動函式,宣告為模板函式  
  3. {     
  4.  TYPE* This = (TYPE*)param;     
  5.  This->_RunThread();     
  6.  return NULL;  
  7.  }  
  8.    
  9.  class MyClass  
  10.  {  
  11.  public:    
  12.  MyClass();   
  13.  void _RunThread();  
  14.    
  15.  private:    
  16.  pthread_t tid;    
  17.  };  
  18.    
  19.  void MyClass::_RunThread()  
  20.  {     
  21.  this->DoSomeThing();     
  22.  }  
  23.    
  24.  MyClass::MyClass()  
  25.  {     
  26.  pthread_create(&tid, NULL, _thread_t<MyClass, &MyClass::_RunThread>, this);  
  27.  }       
  28.    
  29.    
  30.  //函式模版不單可以替換型別本身,還能替換型別的成員函式。  
  31.  //注意:      1、名稱只能是_RunThread,不能在指定模版引數的時候修改;           
  32.  //  2、_RunThread只能是public的,除非把_thread_t定義到MyClass的內部。  
  33.    
  34.    
  35.    

 

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

 

出問題之前的程式碼:

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成員函式適用於可以修改類的原始碼的情況。

 

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