1. 程式人生 > >C++多執行緒(一)——執行緒管理

C++多執行緒(一)——執行緒管理

多執行緒是……/*此處省略一萬字,省略的文字詳細說明了什麼是多執行緒、其歷史及其發展、使用多執行緒的好處和缺點以及C/C++對多執行緒的支援的歷史*/
C++標準庫自C++11標準以來開始支援多執行緒,多執行緒相關的類在thread標頭檔案中,所以使用請先必須#include <thread>

啟動一個執行緒

啟動一個執行緒非常簡單,例程如下:
#include <iostream>
#include <thread>

void test1()
{
	std::cout << "------This message from another thread.-------" << std::endl;
}

int main()
{
	std::thread t(test1);
	std::cout << "-------This message from main thread.-------" << std::endl;
	t.join();
	std::cin.get();
	return 0;
}
上面的程式碼std::thread t(test1)建立並且啟動一個執行緒,所啟動的執行緒將執行test1()函式中的程式碼。 執行緒必須要有一個可呼叫物件來初始化和啟動,這個可呼叫物件可以是函式物件、一般函式、lambda表示式或者成員函式或者由std::bind()產生的可呼叫物件。 下面是啟動執行緒的幾種不同方式的例程:
#include <iostream>
#include <thread>

//一般函式
void test1()
{
	std::cout << "------This message from another thread.-------" << std::endl;
}

void test2(int c)
{
	std::cout << "parameter is " << c << std::endl;
}

class callable
{
public:
	void operator()()
	{
		std::cout << "message from callable object" << std::endl;
	}
	void mem()
	{
		std::cout << "message from member func." << std::endl;
	}
};

int main()
{
	//common function
	std::thread t1(test1);
	//bind
	std::thread t2(std::bind(test2, 10057));

	callable obj;
	//callabel object
	std::thread t3(obj);
	//member function
	std::thread t4(&callable::mem, &obj);
	//lambda function.
	std::thread t5([]() {/*nothing to do.*/});
	std::cout << "-------This message from main thread.-------" << std::endl;
	t1.join();
	t2.join();
	t3.join();
	t4.join();
	t5.join();
	std::cin.get();
	return 0;
}

引數傳遞

傳遞進執行緒中執行的函式也可以有引數,傳遞引數到執行緒中也不是什麼難題,程式碼如下:
void f(int i,std::string const& s);
std::thread t(f,3,”hello”);
但需要注意的是,所有傳遞進執行緒的引數,執行緒首先將實參值複製到其當前執行緒空間中。然後使用的是執行緒空間中的變數。當你的程式碼如下時,最後的結果可能不是你希望的。
void update_data_for_widget(widget_id w,widget_data& data);
void oops_again(widget_id w)
{
widget_data data;
std::thread t(update_data_for_widget,w,data);
display_status();
t.join();
process_widget_data(data);
}
上述程式碼首先將data複製到執行緒空間中,然後再將執行緒空間中data值的引用傳遞到update_data_for_widget()函式中,而不是oop_again函式中原data的引用。要想確保傳遞的是原data的引用,需要用到新的基礎設施:std::ref()。就像下面的程式碼這樣:
std::thread t(update_data_for_widget,w,std::ref(data));
這樣,你的程式碼就能正常工作了。

管理一個執行緒

(1)C++中,執行緒不可被拷貝構造和賦值構造,唯一能夠使用的是移動構造,因此,想要用一個執行緒去初始化別一個執行緒,正確的語法如下:
void some_function();
void some_other_function();
std::thread t1(some_function);
std::thread t2=std::move(t1);
t1=std::thread(some_other_function);
std::thread t3;
t3=std::move(t2);
t1=std::move(t3);
(2)每一個執行緒都有一個唯一的標識,其型別為std::thread::id, 執行緒可以通過std::this_thread::get_id() 函式來獲取其id。 (3)細心的讀者可以注意到了開始執行緒的程式碼片段裡用到了執行緒的成員函式join(),這個函式用於等待執行緒的結束,否則程式碼將阻塞在join()函式的呼叫處。這是因為每個程序都有一個主執行緒,當主執行緒結束時,這個程序就會終止,系統最後會回收這個程序的資源。當主執行緒結束時,其他執行緒就算沒有結束,也會被結束。為了使得其他執行緒執行結束後進程再終止,由於無法主動獲知其他執行緒會在何時結束,所以必須要主執行緒中等待其他執行緒的結束。其他的相關成員函式還有joinable()和detach(),當一個執行緒detach時,將無法join(等待),而執行緒是是否可join,可以用joinable()函式來檢測。當執行緒呼叫detach()函式或者join()函式後,joinable()函式都將返回false。