c++11中多執行緒中Join函式
寫在前面
Join函式作用:
Join thread The function returns when the thread execution has completed.//直到執行緒完成函式才返回 |
在多執行緒的引數傳遞中說到,使用join()函式,我們需要考慮,什麼時候呼叫join()函式,因為如果在join呼叫之前可能會產生中斷,從而跳過此次join()函式的呼叫,下面就來探討一下解決產生這種情況的方法。。。
使用異常處理
#include <iostream> #include <thread> #include <mutex> struct func { int &i; func(int &i_):i(i_){} void operator()() { for (unsigned j = 0; j < 1000000; j++) { //... } } }; void f() { int some_local_state = 0; func my_func(some_local_state); std::thread t(my_func); try { //.. } catch(...) { t.join(); throw; } t.join(); }
程式中使用異常處理的機制,確保即使執行過程中產生異常,程式也會執行join()函式,但是方法仍然存在問題, 因為try/catch只能捕獲輕量級的錯誤
使用RAll方式
我們可以採用資源獲取即初始化方式”(RAII,Resource Acquisition Is Initialization),即提供一個類,對此進行簡單的封裝,在解構函式中採用join
首先我們下來說一下RAll這種方式,資源獲取即初始化(RAII, Resource Acquisition Is Initialization)是指,當你獲得一個資源的時候,不管這個資源是物件、記憶體、檔案控制代碼或者其它什麼,你都會在一個物件的建構函式中獲得它,並且在該物件的解構函式中釋放它。
這種方式應用十分廣泛,可以隨便舉一個栗子:lock_guard類模板的實現:
template<class _Mutex>
class lock_guard
{ // class with destructor that unlocks a mutex
public:
using mutex_type = _Mutex; //using作用個typedef作用相同
explicit lock_guard(_Mutex& _Mtx)
: _MyMutex(_Mtx)
{ // construct and lock 建構函式,並且將互斥量上鎖
_MyMutex.lock();
}
lock_guard(_Mutex& _Mtx, adopt_lock_t) //這裡的adopt_lock_t是一個結構體,其中沒有任何內容
: _MyMutex(_Mtx)
{ // construct but don't lock
}
~lock_guard() _NOEXCEPT //解構函式中將次進行解鎖
{ // unlock
_MyMutex.unlock();
}
lock_guard(const lock_guard&) = delete; //放置編譯器自動生成預設函式,並且禁止呼叫
lock_guard& operator=(const lock_guard&) = delete;
private:
_Mutex& _MyMutex;
};
這樣,我們可以將任意一個互斥量放入次類模板中進行修飾,然後lock_guard就可以在物件析構的時候自動進行unlock操作,防止忘記進行unlock,進而造成死鎖
我們可以自己封裝一個給join函式使用的類:
class thread_guard
{
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;
private:
std::thread &t;
};
將上述的類用到程式中:
#include <iostream>
#include <thread>
#include <mutex>
struct func
{
int &i;
func(int &i_):i(i_){}
void operator()()
{
for (unsigned j = 0; j < 1000000; j++)
{
//...
}
}
};
class thread_guard
{
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;
private:
std::thread &t;
};
void f()
{
int some_local_state = 0;
func my_func(some_local_state);
std::thread t(my_func);
thread_guard g(t);
}
這樣不管是在執行的最後析構thread_guard物件還是出現異常,都會執行解構函式,這樣就可以保證join函式一定可以得到呼叫
參考書籍
《c++併發程式設計實戰》 Anthony Williams著
http://www.cplusplus.com/reference/thread/thread/join/?kw=join