C++11多線程異常

       一旦開始了線程,需要顯示決定要等待線程函數完成或分離它自行完成。如果detach()線程不等待,你要確保通過線程訪問的數據是有效的,直至該線程完成爲止,例如線程函數持有局部變量的指針或引用,且當主函數退出的時候線程未完成,就會出錯,線程函數就會訪問一個已被銷燬的變量,解決方法是數據私有化。join()背後的含義有兩層,一是等待子線程執行完畢,避免主線程先完成,從而導致子線程終止,二是join()會清理與子線程相關聯的存儲器,這樣std::thread對象不再與子線程相關聯,這就意味着你只能對一個給定線程調用一次join(),一旦調用了join(),此std::thread對象不再是可連接的,並且joinable()將返回false。

struct func
{
	int &i;
	func(int& i_):i(i_){}
	void operator()
	{
		for(unsigned int j=0;j<1000;j++)
			do_something(i);             //可能會訪問懸空引用
	}
};
void main()
{
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread my_thread(my_func);
	my_thread.detach();                           //不等待線程完成,可能新的線程仍在運行
}

        如果打算等待該線程,就需要仔細選擇在代碼那個位置調用join(),如果在線程開始調用後,join()之前引發了異常,對join()的調用就會跳過。爲了避免程序在引發異常的時候被終止,還需要在存在異常時調用join()。

struct func;
void main()
{
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread t(my_func);
	try
	{
		do_somthing_in_current_thread();
	}
	catch(...)
	{
		t.join();
		throw;
	}
	t.join();
}

使用try/catch塊很羅嗦,而且容易將作用域弄亂,所以並不是一個理想方案。如果確保線程必須在函數退出前完成是很重要的,無論是因爲它具有對其他局部變量的引用還是任何其他原因,那麼確保這是所有可能退出路徑的情況很重要,無論正常還是異常,希望提供一個簡單明瞭的機制。這種做法之一是使用標準的資源獲取初始化,並提供一個類,在它析構函數中進行join()。

class thread_guard
{
	std::thread t;
public:
	explicit thread_guard(std::thread &t_):t(t_){}
	~thread_guard()
	{
		if(t.joinable())                          //1
		{
			t.join();                         //2
		}
	}
	thread_guard(thread_guard const&)=delete;         //3
	thread_guard& operator=(thread_guard const&)=delete;
}
struct func;
void main()
{
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread my_thread(my_func);
	thread_guard g(t);

	do_something_in_current_thread();                //4              
}

        在主線程執行到末尾4時,局部變量會按照構造函數的逆序銷燬,因此g首先被銷燬,並且析構函數2中線程被結合,即便4中引發異常的情況下也會發生。拷貝構造函數和拷貝賦值運算法被禁止,以確保它們不會被編譯器自動生成,複製或賦值這樣一個對象時比較危險的,因爲它們可能比它要結合的線程的作用域存在的更久。








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章