c++11 多線程編程2 線程管理

簡述

  1. 如何啟動(dòng)線程
    因啟動(dòng)性程需要一個(gè)callable type,可以有函數(shù)指針绕娘,類重載()脓规,lambda三種形式。
    其中類重載()险领,lambda侨舆,有可能會(huì)拿到非運(yùn)行程序的變量,要小心绢陌。
    啟動(dòng)完成后挨下,需要detach或join,否則程序運(yùn)行會(huì)報(bào)錯(cuò)脐湾。引入一個(gè)thread_guard類臭笆。
  2. 給線程傳參數(shù),會(huì)進(jìn)行拷貝操作。
    如果是引用秤掌,則要小心變量的作用域愁铺。他只會(huì)進(jìn)行拷貝操作,而不會(huì)進(jìn)行轉(zhuǎn)換操作闻鉴。
    如果是類茵乱,那么又不需要std::ref。
    如果是moveonly的值孟岛,在傳進(jìn)去之前要std::move
  3. thread 是一個(gè) moveonly的類型瓶竭,這里介紹了scoped_thread (它是一個(gè)線程的'子類')
    還有督勺,當(dāng) thread t1(xxxx); thread t2(x);
    t2 = std::move(t1);會(huì)報(bào)錯(cuò),因?yàn)閠1不見了斤贰。
    (經(jīng)過測試玷氏,沒有辦法再join了,如果有join過腋舌,則不會(huì)報(bào)錯(cuò))
  4. 其它
    獲取當(dāng)前cpu的核數(shù)
    獲取當(dāng)前線程的id
  5. 測試代碼

摘錄

  1. 啟動(dòng)線程的方式

    1. 函數(shù)

      void do_some_work();
      std::thread my_thread(do_some_work);
      
    2. 重載operator()
      經(jīng)過測試盏触,發(fā)現(xiàn)不管用哪種方式都會(huì)調(diào)用 移動(dòng)拷貝構(gòu)造函數(shù)
      只是第一種會(huì)調(diào)用拷貝構(gòu)造函數(shù)一次,移動(dòng)拷貝構(gòu)造函數(shù)一次块饺。別的都是調(diào)用兩次移動(dòng)拷貝構(gòu)造函數(shù)

      class background_task{
          public:
              void operator()()const{
                  //.....
              }
      };
      background_task f;
      std::thread my_thread(f)//書中講會(huì)執(zhí)行background_task這個(gè)類的拷貝構(gòu)造函數(shù)
      //----- 或者
      std::thread my_thread(background_task())//書中講會(huì)執(zhí)行background_task這個(gè)類的移動(dòng)拷貝構(gòu)造函數(shù)
      std::thread my_thread{background_task()}//書中講會(huì)執(zhí)行background_task這個(gè)類的移動(dòng)拷貝構(gòu)造函數(shù)
      
    3. lambda

      std::thread my_thread([](){
          //..
      });
      
    4. thread_guard

      class thread_guard{
          std::thread &t;
      public:
          explicit thread_guard(std::thread &t_):t(t_){}
          ~thread_guard(){
              if ( t.joinable() ){
                  t.join();
              }
          }
          thread_guard(thread_guard const&) = delete;
          thread_guard& operator=(thread_guard const&) = delete;
      };
      //如何使用
      struct func;
      void f(){
          int some_local_state=0;
          func my_func(some_local_state);
          std::thread t(my_func);
          thread_guard g(t);   //會(huì)自動(dòng)join
          do_something_in_current_thread();
      }
      
  2. 傳參數(shù)進(jìn)去

    1. 傳參赞辩,注意作用域
      經(jīng)過測試程序并沒有崩潰,但是struct func的 int &i這個(gè)值的數(shù)據(jù)確實(shí)是跟 some_local_state 的地址一樣授艰。
      所以辨嗽,其實(shí)是有問題的 ,只是暫時(shí)沒崩而已淮腾。

      struct func{
          int &i;
          func(int &i_):i(i_){}
          operator()(){
              for ( int j=0; j<1000000;j++ ){ dosomething(i); }
          }
      };
      void oops(){
          int some_local_state=0;
          func my_func(some_local_state); 
          std::thread my_thread(my_func); //在 thread里運(yùn)行的 &i 數(shù)據(jù)會(huì)沒掉
          my_thread.detach();
      }
      
    2. //std::thread t(func,args...) //args默認(rèn)會(huì)進(jìn)行拷貝操作 是的糟需,會(huì)有
      void f(int i, const std::string &str); //如果是  std::string str 呢? 應(yīng)該也一樣谷朝,因?yàn)閱栴}并沒有解決
      void oops(int somevalue){
          char buff[10] = {0};
          sprintf(buff,"%d",somevalue);
          std::thread t(f,somevalue,buff);  //在這里buff會(huì)從char[]變成 std::string tmp 
                                            //但不知道是何時(shí)操作這個(gè)轉(zhuǎn)換洲押,所以有可能是oops已經(jīng)運(yùn)行完了,
                                            //所以buff值就不知道變成什么樣了圆凰。
                                            //解決方法:std::thread t(f,somevalue,std::string(buff));
          t.detach();
      }
      
    3. 當(dāng)你想把引用傳進(jìn)去杈帐,并在thread里面修改值且期望在外面這個(gè)值也會(huì)有作用,那么:

        void update_data_for_widget(widget_id w, widget_data &data);
        void oops_again(widget_id w){
            widget_data data;
            std::thread t(update_data_for_widget,w,data);
            display_status();
            t.join();
            process_widget(data); //這里的data并沒有改變
                                  //原因:std::thread ( func,... ) ...是會(huì)把那些參數(shù)弄一個(gè)temp然后再傳進(jìn)去 (試試是不是會(huì)調(diào)copy函數(shù))
                                  //解決方法: std::thread t(update_data_for_widget,w,std::ref(data));
        }
    
    1. 如果是一個(gè)類和成員函數(shù) my_x 不會(huì)執(zhí)行拷貝
    class X{
        public:
        void do_xxx_work();
    };
    X my_x;
    std::thread t(&X::do_xxx_work,&my_x);
    
    1. 如果一個(gè)參數(shù)是moveonly的专钉,比如:unique_ptr 需要把那個(gè)ptr 給move掉
    void process_big_object(std::unique_ptr<big_object) p);
    std::unique_ptr<big_object> p(new big_object);
    p->prepare_data(42);
    std::thread t(process_big_object,std::move(p));
    
  3. thread 是一個(gè)move only的值

    1. scoped_thread
      這個(gè)例子放在這里挑童,是因?yàn)橐猻td::move 這也是跟thread_guard的區(qū)別
      thread_guard是傳引用過去。

      class scoped_thread{
          std::thread t;
      public:
          explicit scoped_thread(std::thread t_):t(std::move(t_)){
              if ( !t.joinable() ){
                  throw::std::logic_error("no thread");
              }
          }
          ~scoped_thread(){
              t.join();
          }
          scoped_thread(scoped_thread const &)=delete;
          scoped_thread& operator=(scoped_thread const &)=delete;
      };
      //如何使用
      struct func;
      void f(){
          int some_local_state;
          scoped_thread t(std::thread(func(some_local_state)));
          do_something_in_current_thread();
      }
      
    2. 不過如果有 n個(gè)thread

      void do_work(unsigned id);
      void f(){
          std::vector<std::thread> ths;
          for ( i=0;i<20; i++ ){
              ths.push_pack(std::thread(do_work,i));
          }
          std::for_each(ths.begin(),ths.end(),std::mem_fn(&std::thread::join))
      }
      
    3. 可以認(rèn)為thread是只能移動(dòng)的,他比只能移動(dòng)還多一個(gè)限制

      void func();
      void other_func();
      std::thread t1(func);
      std::thread t2(other_func);
      t1 = std::move(t2);  //這時(shí)還會(huì)報(bào)錯(cuò)跃须,因?yàn)樵瓉淼膖1站叼,無法join
      
    4. 當(dāng)作為函數(shù)的返回值,是允許的 (詳細(xì)可以去查看RVO)

      std::thread f(){
          void some_func();
          return std::thread(some_func);
      }
      std::thread g(){
          void some_func();
          std::thread t(some_func);
          return t;
      }
      
    5. 當(dāng)函數(shù)的參數(shù)為thread 時(shí) 如果是臨時(shí)變量需要move

      void f(std::thread t);
      //怎么傳參:
      void g(){
          //1
          void some_func();
          f(std::thread(some_func));
          //2
          std::thread t(some_func);
          f(std::move(t));
      }
      
    6. 附:golang 的 defer很好用,來個(gè)c++版的

      template <typename F> struct privDefer {
          F f;
          privDefer(F fp) : f(fp) {}
          ~privDefer() { f(); }
      };
      template <typename F> privDefer<F> defer_func(F f) { return privDefer<F>(f); }
      #define BUFFALO_DEFER_1(x, y) x##y
      #define BUFFALO_DEFER_2(x, y) BUFFALO_DEFER_1(x, y)
      #define BUFFALO_DEFER_3(x) BUFFALO_DEFER_2(x, __COUNTER__)
      #define defer(code) auto BUFFALO_DEFER_3(_glngbll_defer_val_) = defer_func([&]() { code; })
      //-------------- 使用
      int main(){
          int *a = new int;
          defer(delete a);
          return 0;
      }
      
  4. 雜項(xiàng)

    1. 獲取此機(jī)器能并行處理多少線程 等于cpu的核數(shù)
      std::thread::hard_concurrency();
      
2. 獲取thread id
    ```
    //獲取id 類型是 std::thread::id;
    std::thread::id master_id;
    if ( std::this_thread::get_id() == master_id){
    }
    ```
  1. 測試代碼
    1. 測試拷貝多少次

      #include <iostream>
      #include <thread>
      typedef struct Big_t {
          Big_t() = default;
          Big_t(const Big_t &other) { std::cout << "Big_t" << std::endl; }
          void func() {}
          int v[100];
      } Big_t;
      void funcReference(const Big_t &big) {}
      void funcValue(Big_t big) {}
      int main(int argc, const char *argv[]) {
          Big_t big;
          std::cout << "------------------std::ref 沒有construct" << std::endl;
          std::thread t(funcReference, std::ref(big));
          t.join();
          std::cout << "------------------2次construct" << std::endl;
          std::thread t1(funcReference, big);
          t1.join();
          std::cout << "funcValue ------------------std::ref 1次" << std::endl;
          std::thread t10(funcValue, std::ref(big));
          t10.join();
          std::cout << "------------------ 三次" << std::endl;
          std::thread t11(funcValue, big);
          t11.join();
          std::cout << "------------------ class 沒有" << std::endl;
          std::thread t2(&Big_t::func, &big);
          t2.join();
          return 0;
      }
      
    2. 測試拷貝函數(shù)和移動(dòng)拷貝函數(shù)

      #include <iostream>
      #include <thread>
      class background_task {
       public:
          background_task() = default;
          background_task(const background_task &other) {
              std::cout << "background_task_construct 1" << std::endl;
          }
          background_task &operator=(const background_task &other) {
              std::cout << "background_task_construct 2" << std::endl;
              return *this;
          }
          background_task(background_task &&other) {
              std::cout << "background_task_construct 3" << std::endl;
          }
          background_task &operator=(background_task &&other) {
              std::cout << "background_task_construct 4" << std::endl;
              return *this;
          }
          virtual ~background_task() = default;
       public:
          void operator()() const { std::cout << "thread func" << std::endl; }
      };
      int main(int argc, const char *argv[]) {
          {
              background_task f;
              std::cout << "thread t(f)" << std::endl;
              std::thread t(f);
              t.join();
          }
          std::cout << "-----------------" << std::endl;
          {
              std::cout << "thread t(background_task)" << std::endl;
              std::thread t((background_task()));
              t.join();
          }
          std::cout << "-----------------" << std::endl;
          {
              std::cout << "thread t{background_task()}" << std::endl;
              std::thread t{background_task()};
              t.join();
          }
          std::cout << "-----------------" << std::endl;
          return 0;
      }
      
    3. 看move only的值是否會(huì)因沒有任何值關(guān)聯(lián)時(shí)會(huì)報(bào)錯(cuò)

      #include <iostream>
      #include <thread>
      void func() { std::cout << "func" << std::endl; }
      void otherfunc() { std::cout << "otherfunc" << std::endl; }
      int main(int argc, const char *argv[]) {
          std::thread t1(func);
          t1.join();
          std::thread t2 = std::move(t1);
          t1 = std::thread(otherfunc);
          t1.join();
          std::thread t3;
          t3 = std::move(t2);
          t1 = std::move(t3);
          return 0;
      }
      
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末菇民,一起剝皮案震驚了整個(gè)濱河市尽楔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌玉雾,老刑警劉巖翔试,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異复旬,居然都是意外死亡垦缅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門驹碍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來壁涎,“玉大人凡恍,你說我怎么就攤上這事≌颍” “怎么了嚼酝?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長竟坛。 經(jīng)常有香客問我闽巩,道長霹琼,這世上最難降的妖魔是什么俭识? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任三妈,我火速辦了婚禮俘闯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘顿仇。我一直安慰自己寄症,他們只是感情好绣硝,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布率碾。 她就那樣靜靜地躺著叔营,像睡著了一般。 火紅的嫁衣襯著肌膚如雪所宰。 梳的紋絲不亂的頭發(fā)上绒尊,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機(jī)與錄音歧匈,去河邊找鬼垒酬。 笑死砰嘁,一個(gè)胖子當(dāng)著我的面吹牛件炉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播矮湘,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼斟冕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缅阳?” 一聲冷哼從身側(cè)響起磕蛇,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎十办,沒想到半個(gè)月后秀撇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡向族,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年呵燕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片件相。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡再扭,死狀恐怖氧苍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泛范,我是刑警寧澤让虐,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站罢荡,受9級特大地震影響赡突,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜区赵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一麸俘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惧笛,春花似錦从媚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至各谚,卻和暖如春紧憾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昌渤。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工赴穗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膀息。 一個(gè)月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓般眉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親潜支。 傳聞我的和親對象是個(gè)殘疾皇子甸赃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 接著上節(jié) condition_varible ,本節(jié)主要介紹future的內(nèi)容冗酿,練習(xí)代碼地址埠对。本文參考http:/...
    jorion閱讀 14,776評論 1 5
  • 最近一直在看游雙的《高性能linux服務(wù)器編程》一書,下載鏈接: http://download.csdn.net...
    張小方閱讀 1,185評論 0 2
  • 接著上上節(jié) thread 裁替,本節(jié)主要介紹mutex的內(nèi)容项玛,練習(xí)代碼地址。<mutex>:該頭文件主要聲明了與互斥量...
    jorion閱讀 12,471評論 2 4
  • 接著上節(jié) atomic弱判,本節(jié)主要介紹condition_varible的內(nèi)容襟沮,練習(xí)代碼地址。本文參考http://...
    jorion閱讀 8,471評論 0 7
  • 生產(chǎn)者消費(fèi)者問題是多線程并發(fā)中一個(gè)非常經(jīng)典的問題,相信學(xué)過操作系統(tǒng)課程的同學(xué)都清楚這個(gè)問題的根源臣嚣。本文將就四種情況...
    張霸天閱讀 273評論 0 0