1-1. C++併發(創建線程)

創建線程
案例1:如何創建線程

#include <thread>
void do_some_work();
std::thread my_thread1(do_some_work);//函數對象會複製到新線程的存儲空間中,函數對象的執行和調用都在線程的內存空間中進行

class background_task
{
public:
	void operator()() const
	{
		do_something();
		do_something_else();
	}
};
background_task f;
std::thread my_thread2(f);//可調用對象f,必須對()符號進行重載
std::thread my_thread3((background_task()));//std::thread my_thread(background_task())不可用,使用臨時變量該語句會被編譯器判定爲函數聲明
std::thread my_thread4{background_task()};//新的初始化語法

案列2:線程的加入、分離
一旦開始了線程,你需要顯式地決定是要等待它完成,還是讓它自行運行。如果你在std::thread對象被銷燬之前還未決定,那麼你的程序會被終止(std::thread的析構函數調用std::terminate終端線程,這將會造成exception)。如果選擇等待線程,則應該將join命令放到合適的位置,因爲在等待線程期限,主線程是不會做其他事情的。如果選擇分析線程,則應該注意子線程使用的資源(例如引用和指針)是否在主線程結束後還是有效,舉例請看案例3。

#include <iostream>
#include <thread>
void do_some_work()

int main()
{
	cout<<"run do_some_work"<<endl;
	std::thread my_thread(do_some_work);//創建線程
	... //應該在join之前進行一些其他的工作防止浪費線程資源
	my_thread.join();//加入線程,主線程會等待子線程結束
	//my_thread.detach()  分離線程,主線程不會等待子線程結束
	return 0;
}

案例3:線程分離需要注意的情況
在下面的例子中,當主線程退出時,和my_thread相關聯的線程可能仍然在運行中,但是當線程調用do_something(i)的時候,就會訪問爲銷燬的i變量,這將會造成異常。而另外一種常見情況就是被分離的線程使用了被釋放的指針,從而造成錯誤。常見的處理方式就是將數據複製到該線程中而不是共享數據。

#include <iostream>
#include <stream>
struct func
{
	int& i;
	func(int& i_):i(i_){}
	void operator()()
	{
		for(unsigned j=0; j<10000; ++j)
		{
			do_something(i);
		}
	}
}
int main()
{
	int local_state = 0;
	func my_func(local_state);
	std::thread my_thread(my_func);
	my_thread.detach();
}

案例4:異常情況下的等待
在前面我們已經講過了,開始線程後需要顯式地加入或者分析線程,避免由此造成的異常。另外,當主線程中出現異常時也要對子線程進行管理。但是這樣的結構很囉嗦,因此可以用更簡潔的方式,資源獲取即初始化(RAII)

#include <iostream>
#include <thread>
struct func;
void f()
{
	int local_state=0;
	func my_func(local_state);
	std::thread my_thread(my_func);
	try
	{
		do_something();
	}
	catch(...)
	{
		my_thread.join();  //異常情況下的join
		throw;
	}
	my_thread.join(); //正常情況下的join
}

案例5:資源獲取即初始化(RAII)
將線程的控制放在一個類裏面,從而可以保險的進行操作防止join操作的遺漏。

#include <iostream>
#include <thread>
class thread_guard
{
	std::thread& t;
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;
};
struct func;
int main()
{
	int local_state=0;
	func my_func(local_state);
	std::thread t(my_func);
	thread_guard g(t);
	do_something();
}

*本文大部分內容整理自《C++併發編程實戰》

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