[C++11 併發編程] 02 - join

1. 等待線程完成

若不等待線程完成,我們就需要確保該線程訪問的數據都是有效的,直到該線程完成爲止。比如如下代碼,線程函數持有局部變量的指針或引用,當函數退出時,線程尚未執行完成。

#include <thread>
#include <iostream>


// 線程持有局部變量的指針
struct func
{
	int *i;
	func(int *i_) : i(i_){
	
	}

	void operator()()
	{
		for (unsigned j = 0; j < 100000; ++j)
		{
			*i = j; // 訪問非法地址
		}
	}
};

// 不等待線程執行完成就退出
void oops()
{
	int some_local_state = 0;
	func my_func(&some_local_state);
	std::thread my_thread(my_func);
	my_thread.detach();
}

int _tmain(int argc, _TCHAR* argv[])
{
	oops();

	return 0;
}

要避免這種情況,需要使用std::thread實例的join()來替換my_thread.detach()的調用,這樣就可以保證在函數退出前,線程已經結束。對一個給定的線程,只能調用一次join(),一旦調用了join(),此std::thread對象不再是可連接的,如果調用其的joinable()將返回false。

2. 在異常環境下的等待

我們需要在線程對象被銷燬前調用join或detach方法,如果要detach,通常在線程啓動後就立即調用detach方法。如果打算等待該線程,就需要仔細的選擇在哪個位置調用join。如果在線程開始之後,調用join之前發生了異常,則可能跳過對join的調用。

爲了避免應用程序在引發異常的時候被終止,你需要異常時也調用join。

void do_something_in_current_thread()
{
	throw("error");
}

// 不等待線程執行完成就退出
void oops()
{
	int some_local_state = 0;
	func my_func(&some_local_state);
	std::thread my_thread(my_func);

	try
	{
		do_something_in_current_thread();
	}
	catch (const char *err_msg)
	{
		my_thread.join();
		throw;
	}
	
	my_thread.join();
}
try/catch塊可以確保無論函數時正常退出還是異常退出,都調用了線程的join方法,但是try/catch塊看起來很囉嗦,也容易導致作用於混亂,更簡單的辦法是使用RAII-Resource Acquisition Is Initialization並提供一個類,在析構函數中調用join():

class thread_guard
{
	std::thread& t;
public:
	explicit thread_guard(std::thread& t_) :
		t(t_)
	{
	}

	// 析構函數中檢查線程是否還未被join,若沒有,則調用
	~thread_guard()
	{
		if (t.joinable())
		{
			t.join();
		}
	}

	// 將拷貝後賦值運算符標記爲=delete以避免編譯器自動生成,複製或賦值這樣一個對象可能很危險,因爲它可能比它要結合的線程的作用域存在得更久。
	thread_guard(thread_guard const&) = delete;
	thread_guard& operator=(thread_guard const&) = delete;
}



發佈了109 篇原創文章 · 獲贊 4 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章