c++併發程式設計之thread::join()和thread::detach()
阿新 • • 發佈:2018-12-21
thread::join(): 阻塞當前執行緒,直至 *this 所標識的執行緒完成其執行。*this 所標識的執行緒的完成同步於從 join() 的成功返回。
該方法簡單暴力,主執行緒等待子程序期間什麼都不能做。thread::join()會清理子執行緒相關的記憶體空間,此後thread object將不再和這個子執行緒相關了,即thread object不再joinable了,所以join對於一個子執行緒來說只可以被呼叫一次,為了實現更精細的執行緒等待機制,可以使用條件變數等機制。
#include <iostream> #include <thread> #include <chrono> void foo() { std::cout << "foo is started\n"; // 模擬昂貴操作 std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "foo is done\n"; } void bar() { std::cout << "bar is started\n"; // 模擬昂貴操作 std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "bar is done\n"; } int main() { std::cout << "starting first helper...\n"; std::thread helper1(foo); std::cout << "starting second helper...\n"; std::thread helper2(bar); std::cout << "waiting for helpers to finish...\n" << std::endl; helper1.join(); helper2.join(); std::cout << "done!\n"; }
starting first helper...
starting second helper...
foo is started
waiting for helpers to finish...
bar is started
foo is done
bar is done
done!
異常環境下join,假設主執行緒在一個函式f()裡面建立thread object,接著f()又呼叫其它函式g(),那麼確保在g()以任何方式下退出主執行緒都能join子執行緒。如:若g()通過異常退出,那麼f()需要捕捉異常後join.
#include<iostream> #include<boost/thread.hpp> void do_something(int& i){ i++; } class func{ public: func(int& i):i_(i){} void operator() (){ for(int j=0;j<100;j++) do_something(i_); } public: int& i_; }; void do_something_in_current_thread(){} void f(){ int local=0; func my_func(local); boost::thread t(my_func); try{ do_something_in_current_thread(); } catch(...){ t.join();//確保在異常條件下join子執行緒 throw; } t.join(); } int main(){ f(); return 0; }
上面的方法看起來笨重,有個解決辦法是採用RAII(資源獲取即初始化),將一個thread object通過棧物件A管理,在棧物件A析構時呼叫thread::join.按照區域性物件析構是構造的逆序,棧物件A析構完成後再析構thread object。如下:
#include<iostream> #include<boost/noncopyable.hpp> #include<boost/thread.hpp> using namespace std; class thread_guard:boost::noncopyable{ public: explicit thread_guard(boost::thread& t):t_(t){} ~thread_guard(){ if(t_.joinable()){//檢測是很有必要的,因為thread::join只能呼叫一次,要防止其它地方意外join了 t_.join(); } } //thread_guard(const thread_guard&)=delete;//c++11中這樣宣告表示禁用copy constructor需要-std=c++0x支援,這裡採用boost::noncopyable已經禁止了拷貝和複製 //thread_guard& operator=(const thread_guard&)=delete; private: boost::thread& t_; }; void do_something(int& i){ i++; } class func{ public: func(int& i):i_(i){} void operator()(){ for(int j=0;j<100;j++) do_something(i_); } public: int& i_; }; void do_something_in_current_thread(){} void fun(){ int local=0; func my_func(local); boost::thread t(my_func); thread_guard g(t); do_something_in_current_thread(); } int main(){ fun(); return 0; }
thread::detach(): 從 thread 物件分離執行的執行緒,允許執行獨立地持續。一旦執行緒退出,則釋放所有分配的資源。呼叫 detach
後, *this 不再佔有任何執行緒。
#include <iostream>
#include <chrono>
#include <thread>
void independentThread()
{
std::cout << "Starting concurrent thread.\n";
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Exiting concurrent thread.\n";
}
void threadCaller()
{
std::cout << "Starting thread caller.\n";
std::thread t(independentThread);
t.detach();
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Exiting thread caller.\n";
}
int main()
{
threadCaller();
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "back to main.\n";
}
執行結果:
Starting thread caller. Starting concurrent thread. Exiting thread caller. Exiting concurrent thread. back to main.
如果註釋掉main函式裡的std::this_thread::sleep_for(std::chrono::seconds(5)); 即不等待independentThread 執行完。執行結果如下:
Starting thread caller. Starting concurrent thread. Exiting thread caller. back to main.