[原]C++新標准之std::thread


概覽

從C++11開始提供了線程的支持,終於可以方便的編寫跨平台的線程代碼了。除了std::thread類,還提供了許多其它便利同步的機制,本篇總結是C++11學習筆記系列的首篇總結。

std::thread

std::thread定義在<thread>中,提供了方便的創建線程的功能。

類定義

  1. class thread 
  2. { 
  3. public
  4. thread() noexcept
  5. thread( thread&& other ) noexcept
  6. template< class Function, class... Args > 
  7. explicit thread( Function&& f, Args&&... args ); 
  8. thread(const thread&) = delete
  9. ~thread(); 
  10. thread& operator=( thread&& other ) noexcept
  11. bool joinable() const noexcept
  12. std::thread::id get_id() const noexcept
  13. native_handle_type native_handle()
  14. void join()
  15. void detach()
  16. void swap( thread& other ) noexcept
  17. static unsigned int hardware_concurrency() noexcept
  18. }; 

從定義中我們可以得知:

各個成員函數的簡單介紹

例子

因為thread類比較簡單,我們通過幾個例子來學習。

  • 支持移動語義,但不支持拷貝語義
  1. #include <thread> 
  2. void some_function() {} 
  3. void some_other_function() {} 
  4. int main() 
  5. std::thread t1(some_function); // 構造一個thread對象t1 
  6. std::thread t2 = std::move(t1); // 把t1 move給另外一個thread對象t2,t1不再管理之前的線程了。 
  7. // 這句不需要std::move(),從臨時變量進行移動是自動和隱式的。調用的是operator=(std::thread&&) 
  8. t1 = std::thread(some_other_function); 
  9. std::thread t3; 
  10. t3 = std::move(t2); // 把t2 move給t3 
  11. // 把t3 move給t1,非法。因為`t1`已經有了一個相關的線程,會調用`std::terminate()`來終止程序。 
  12. t1 = std::move(t3); 
  • 通過調用join()成員函數來等待線程結束
  1. #include <iostream> 
  2. #include <thread> 
  3. #include <chrono> 
  4.  
  5. void foo()  
  6. std::this_thread::sleep_for(std::chrono::seconds(1)); 
  7.  
  8. int main() 
  9. std::cout << "starting first helper...\n"
  10. std::thread helper1(foo)
  11. helper1.join(); 
  • 傳遞參數給線程函數
  1. void f(int i, std::string const& s)
  2. std::thread t(f, 3 "hello")

注意:參數會以默認的方式被復制到內部存儲空間,直到使用的時候才會轉成對應的類型。

下面的例子有問題嗎?有什么問題?

  1. void f(int i, std::string const& s)
  2. void oops(int some_param) 
  3. char buffer[1024]; 
  4. sprintf(buffer, "%i", some_param); 
  5. std::thread t(f, 3, buffer)
  6. t.detach(); 

局部變量buffer的指針會被傳遞給新線程,如果oops()buffer被轉換成string之前退出,那么會導致未定義的行為。解決之道是在構造std::thread的時候傳遞string變量。std::thread t(f, 3, std::string(buffer));

可以使用std::ref()來顯示表明要傳遞引用,就像std::bind()那樣。

  1. std::thread t(update_data_for_widget, w, std::ref(data))
  • 使用類的成員函數作為線程參數
  1. #include <thread> 
  2. #include <string> 
  3. class CRunner 
  4. { 
  5. public
  6. void run0(){} 
  7. void run1(int a) {} 
  8. void run2(int a, int b) const {} 
  9. int run3(int a, char b, const std::string& c) {return 0;} 
  10. int run4(int& a, double b, float c, char d) { ++a; return 0; } 
  11. static void run_static(int a) {} 
  12. }; 
  13.  
  14. int main() 
  15. CRunner runner; 
  16. int a = 0
  17. // 使用std::mem_fun,需要傳指針 
  18. std::thread t0(std::mem_fun(&CRunner::run0), &runner)
  19. // 使用std::mem_fun_ref,可以傳引用或副本 
  20. std::thread t1(std::mem_fun_ref(&CRunner::run1), std::ref(runner), 1)
  21. // std::thread t1(std::mem_fun_ref(&CRunner::run1), runner, 1); 
  22.  
  23. // 使用std::mem_fn,std::mem_fn支持多於一個參數的函數,std::mem_fun不支持。 
  24. std::thread t2(std::mem_fn(&CRunner::run2), std::ref(runner), 1, 2)
  25. // 使用std::bind + std::mem_fn 
  26. std::thread t3(std::bind(std::mem_fn(&CRunner::run3), &runner, 1, 2, "data"));  
  27. // 使用std::mem_fn,注意std::ref的用法,如果不用std::ref行不行? 
  28. std::thread t4(std::mem_fn(&CRunner::run4), &runner, std::ref(a), 2.2, 3.3f, 'd');  
  29. // 使用類的靜態函數 
  30. std::thread t5(&CRunner::run_static, 1)
  31.  
  32. t0.join(); 
  33. t1.join(); 
  34. t2.join(); 
  35. t3.join(); 
  36. t4.join(); 
  37. t5.join(); 

注:

更多

雖然在之前的例子中的函數有返回值,但是我們卻不能獲得,想獲得返回值我們需要使用std::future,關於std::future的總結,后續會慢慢補充,敬請期待。

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM