日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

《线程管理:线程基本操作》

發布時間:2023/12/1 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《线程管理:线程基本操作》 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 線程管理
    • 啟動線程與(不)等待線程完成
    • 特殊情況下的等待(使用trycath或rall)
    • 后臺運行線程

線程管理

啟動線程與(不)等待線程完成

提供的函數對象被復制到新的線程的存儲空間中,函數對象的執行和調用都在線程的內存空間中進行。

class background_task { public:void operator()() const{do_something();do_something_else();} };background_task f;std::thread my_thread(f);

注意,如果傳遞的是一個臨時變量,而不是一個命名變量,cpp編譯器會將其解析為函數聲明,而不是類型對象的定義。

當我們不等一個線程返回時,我們需要先將數據復制到線程中,這樣就不會產生訪問到已經銷毀的變量的問題了。

就如下所示,函數已經返回,線程依舊能夠訪問到局部變量

struct func {int& i;func(int& i_) : i(i_) {}void operator() (){for (unsigned j=0 ; j<1000000 ; ++j){// 1 潛在訪問隱患:空引用do_something(i);}} }; void oops() {int some_local_state=0;func my_func(some_local_state);std::thread my_thread(my_func);my_thread.detach(); } // 2 不等待線程結束 // 3 新線程可能還在運行

oops函數執行完成時,線程中的函數還在執行,還會訪問已經銷毀了的some_local_state變量,因為它持有的是該變量的指針。所以需要將數據復制到線程中,原始對象被銷毀也不妨礙線程中變涼了。當然,使用訪問局部變量的函數去創建線程不是很好。

此外,可以通過join()函數來確保線程在主函數完成前結束,可以確保局部變量在線程完成后才銷毀。

注意,只能對一個線程使用一次join,一旦使用過join,thread對象就不能再次匯入。

特殊情況下的等待(使用trycath或rall)

對于一個未銷毀的thread對象,如果想分離線程,在線程啟動后直接使用detach()進行分離。如果想等待線程,就需要思考好join位置,也要考慮拋出異常給join帶來的生命周期問題。

如下:使用了try/catch塊確保線程退出后函數才結束

struct func; //定義代碼上面有 void f() {int some_local_state = 0;func my_func(some_local_state);std::thread t(my_func);try{do_something_in_current_thread();}catch(...){t.join();throw;}t.join(); }

接下來介紹使用RALL等待線程完成

class thread_guard {std::thread& t; public:explicit thread_guard(std::thread& t_): t(t_) {}~thread_guard(){if(t.joinable()) //1t.join(); //2}thread_guard(thread_guard const&) = delete; //3thread_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);do_something_in_current_thread(); } //4

當線程執行到4,局部對象就要被逆序銷毀了。所以,對象g第一個被銷毀,線程在析構函數中,判斷是可join的,隨之執行join。所以即使do_something_in_current_thread函數跑出異常,這個銷毀依舊會發生。

還有個需要注意的地方,拷貝構造函數和拷貝賦值操作做標記為 =delete,編譯器不會自動生成。因為直接對對象進行拷貝或者賦值可能會丟失已經join的線程。

如果不想等待線程結束,可以分離線程,從而避免異常。不過這就打破了線程與std::thread對象聯系。即使線程仍然在后臺運行,detach操作也能確保std::terminate()在std::thread對象銷毀時才調用。

后臺運行線程

一個線程在后臺運行,就不能與主線程直接交互,分離的線程也不能join,不過c++保證,當線程退出時,相關資源能夠正確回收。

分離線程又稱守護線程。在UNIX中,守護線程指的是沒有任何顯式接口,并在后臺運行的線程。特點是長時間運行。

下面介紹一下分離線程的使用場景:

讓一個文字處理應用同時編輯多個文檔。每個文檔窗口看起來完全獨立,每個窗口也都有自己獨立的菜單選項,但他們卻運行在同一個應用實例中。一種內部處理方式是,讓每個文檔處理窗口擁有自己的線程。每個線程運行同樣代碼,并隔離不同窗口處理的數據。所以沒打開一個文檔就要啟動一個新線程,因為是對獨立文檔進行操作,所以沒有必要等待其他線程完成,可以讓文檔處理窗口運行在分離線程上。

void edit_document(std::string const& filename) {open_document_and_display_gui(filename);while(!done_editing()){user_command cmd = get_user_input();if(cmd.type == open_new_document){std::string const new_name = get_filename_from_user();std::thread t(edit_document,new_name); //1t.detach(); //2}else{process_user_input(cmd);}}}

用戶選擇打開一個新文檔,需要啟動一個新線程去打開新文檔(如step1),并分離線程(如step2)。與當前線程做出的操作一樣,新線程只不過是打開另一個文件而已。所以,edit_document函數可以復用,并通過傳參的形式打開新的文件。

總結

以上是生活随笔為你收集整理的《线程管理:线程基本操作》的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。