2019年3月4日 星期一

用 c++ 寫簡單的多執行緒 thread

用 c++  實現多執行緒在 linux 系統下用的是 pthread library, thread 的建構方法除了可以用一般函式(function)指標當參數也可以用函式物(functor):
// thread.c
#include <thread> //  要使用 std::thread 的 STL(標準範本程式庫)
using namespace std;// 函式庫使用 std::
struct functor { // 宣告一個函式物件的新類型
     void  operator ( ) (void) {  printf("This is  a functor\n");  } // 函式物( )的運算方法
}; //新類型宣告結束要加上分號
void function1 (void)  {  printf("This is  a function\n"); } // 一般的函式
void function2 (int i)   {  printf("parameter is  %d\n", i); } // 另一種帶參數的一般函式
int main( ) {
    functor F; // 實例化一個函式物件
     thread   f1( function1 );//函式名稱本身就是一個指標, 用一般函式建構執行緒 f1
     thread   f2(function2, 3);//用帶參數函式建構執行緒 f2, 參數隨後附加在函式名稱之後
     thread   f3( F );// 另外也可以使用函式物建構執行緒 f3
     printf("main thread\n");
     f1.join( );  // 等待 f1 執行緒結束
     f2.join( ); // 等待 f2 執行緒結束
     f3.join( ); // 等待 f3 執行緒結束
}
用 g++   thread.c  -pthread 編譯並執行:
main thread
This is  a function
This is  a functor
parameter is  3

要注意的是執行緒用 thread 建構完成, 執行順序由作業系統排定, 一旦宣告完, 隨時都會執行, 上述程式當 f3 建構完成後, 包括主程式 main 在內, 實際上共有 4 條執行緒同時在運作.  thread 之間溝通橋樑最簡單的方法是利用整體變數當作共享資源互相聯絡. 但缺點是很容易產生不可預期結果,嚴重者造成鎖死當機(dead lock). 因此為了掌控 thread 的運作與穩定, 最好是透過實例化的 promise 物件, 透過它內部的方法 get_future 先獲取配對的 future 物件並傳給執行緒, 這個配對的 promise 與 future 就可以共同處理資源共享的問題, 只要簡單用 promise 所提供的方法 .set_value( )設定未來值, 再由 future 的方法 .get( ) 取得結果, 來掌控 thread 內部程式碼的執行順序. 參考範例:
// promise.c
#include <future>// 要使用std::thread , std::promise,  std::future 的 STL
using namespace std;   // 函式庫使用 std::
void function3(future<int> &f){//攜帶未來參數 f 的一般函式,參數要參考物件
    int x = f.get(); // 未來用 .get( ) 方法讀取設定值, 執行緒在此等待承諾的設定完成(fullfill)
    printf("parameter is  %d\n", x);
}
int main( ) {
     promise<int> p; // 宣告一個 promise 物件, 承諾標的物型態是 <int>
     auto  f = p.get_future();// 從 promise 物件的方法產生配對的 future 物件 f
     thread f2(function3, ref(f)); // 參考 std::ref(result) , 把它傳給函式啟動新執行緒
     printf("main thread\n");
     p.set_value(10); // 承諾設定給執行緒的未來值
     f2.join( ); // 等待 f2 執行緒結束
}
用 g++  promise.c  -pthread  &&  ./a.out 編譯並執行:
main thread
parameter is  10

要建構執行緒, 如果所用的函式指標放在整體區域, 通常不會有問題,但要注意的是, 如果放在類型內去建構或是呼叫到類型內的成員(包含變數及方法)時, 這些成員也必須具有 static (實體) 屬性, 換句話說 static 方法只能取用物件內的 static 成員. 實際上類型內所宣告的 static 成員的實體是放在整體區域,並不會因物件消滅而消失, 這也是 static 成員要放在類型外面去宣告的主要原因. 而且 static 方法內並沒有 this 這個指標或是 *this 物件可用, 也就是說類型內的 static 方法或是變數跟物件是搭不上的. 在類型內 static 方法只是一個符號連結(forward declaraction), 放在類型內的好處就是讓 static 成員也需遵循類型的區域守則,透過放在類型內的 private: 或 protected: 區域加以保護而不受整體區域變處所干擾.

沒有留言: