1. 程式人生 > >C++11併發之std::thread

C++11併發之std::thread

知識連結: C++11 併發之std::atomic 本文概要:
1、成員型別和成員函式。 2、std::thread 建構函式。 3、非同步。 4、多執行緒傳遞引數。 5、join、detach。 6、獲取CPU核心個數。 7、CPP原子變數與執行緒安全。 8、lambda與多執行緒。 9、時間等待相關問題。 10、執行緒功能拓展。 11、多執行緒可變引數。 12、執行緒交換。 13、執行緒移動。 std::thread 在 #include<thread> 標頭檔案中宣告,因此使用 std::thread 時需要包含 #include<thread>標頭檔案。
1、成員型別和成員函式。 成員型別:

成員函式:

Non-member overloads:

2、std::thread 建構函式。 如下表: (1).預設建構函式,建立一個空的 thread 執行物件。
(2).初始化建構函式,建立一個 thread 物件,該 thread 物件可被 joinable,新產生的執行緒會呼叫 fn 函式,該函式的引數由 args 給出。 (3).拷貝建構函式(被禁用),意味著 thread 不可被拷貝構造。 (4).move 建構函式,move 建構函式,呼叫成功之後 x 不代表任何 thread 執行物件。 注意:可被 joinable 的 thread 物件必須在他們銷燬之前被主執行緒 join 或者將其設定為 detached。 std::thread 各種建構函式例子如下:
<span style="font-size:12px;">#include<iostream>
#include<thread>
#include<chrono>
using namespace std;
void fun1(int n)  //初始化建構函式
{
	cout << "Thread " << n << " executing\n";
	n += 10;
	this_thread::sleep_for(chrono::milliseconds(10));
}
void fun2(int & n) //拷貝建構函式
{
	cout << "Thread " << n << " executing\n";
	n += 20;
	this_thread::sleep_for(chrono::milliseconds(10));
}
int main()
{
	int n = 0;
	thread t1;               //t1不是一個thread
	thread t2(fun1, n + 1);  //按照值傳遞
	t2.join();
	cout << "n=" << n << '\n';
	n = 10;
	thread t3(fun2, ref(n)); //引用
	thread t4(move(t3));     //t4執行t3,t3不是thread
	t4.join();
	cout << "n=" << n << '\n';
	return 0;
}
執行結果:
Thread 1 executing
n=0
Thread 10 executing
n=30</span>
3、非同步。 例如:
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
void show()
{
	cout << "hello cplusplus!" << endl;
}
int main()
{
	//棧上
	thread t1(show);   //根據函式初始化執行
	thread t2(show);
	thread t3(show);
	//執行緒陣列
	thread th[3]{thread(show), thread(show), thread(show)}; 
	//堆上
	thread *pt1(new thread(show));
	thread *pt2(new thread(show));
	thread *pt3(new thread(show));
	//執行緒指標陣列
	thread *pth(new thread[3]{thread(show), thread(show), thread(show)});
	return 0;
}</span>
4、多執行緒傳遞引數。 例如:
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
void show(const char *str, const int id)
{
	cout << "執行緒 " << id + 1 << " :" << str << endl;
}
int main()
{
	thread t1(show, "hello cplusplus!", 0);
	thread t2(show, "你好,C++!", 1);
	thread t3(show, "hello!", 2);
	return 0;
}
執行結果:
執行緒 1執行緒 2 :你好,C++!執行緒 3 :hello!
:hello cplusplus!</span>
發現,執行緒 t1、t2、t3 都執行成功! 5、join、detach。 join例子如下:
<span style="font-size:12px;">#include<iostream>
#include<thread>
#include<array>
using namespace std;
void show()
{
	cout << "hello cplusplus!" << endl;
}
int main()
{
	array<thread, 3>  threads = { thread(show), thread(show), thread(show) };
	for (int i = 0; i < 3; i++)
	{
		cout << threads[i].joinable() << endl;//判斷執行緒是否可以join
		threads[i].join();//主執行緒等待當前執行緒執行完成再退出
	}
	return 0;
}
執行結果:
hello cplusplus!
hello cplusplus!
1
hello cplusplus!
1
1</span>
總結: join 是讓當前主執行緒等待所有的子執行緒執行完,才能退出。 detach例子如下:
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
void show()
{
	cout << "hello cplusplus!" << endl;
}
int main()
{
	thread th(show);
	//th.join(); 
	th.detach();//脫離主執行緒的繫結,主執行緒掛了,子執行緒不報錯,子執行緒執行完自動退出。
	//detach以後,子執行緒會成為孤兒執行緒,執行緒之間將無法通訊。
	cout << th.joinable() << endl;
	return 0;
}
執行結果:
hello cplusplus!
0</span>
結論: 執行緒 detach 脫離主執行緒的繫結,主執行緒掛了,子執行緒不報錯,子執行緒執行完自動退出。 執行緒 detach以後,子執行緒會成為孤兒執行緒,執行緒之間將無法通訊。 6、獲取CPU核心個數。 例如:
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
int main()
{
	auto n = thread::hardware_concurrency();//獲取cpu核心個數
	cout << n << endl;
	return 0;
}
執行結果:
8</span>
結論: 通過  thread::hardware_concurrency() 獲取 CPU 核心的個數。 7、CPP原子變數與執行緒安全。 問題例如
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
const int N = 100000000;
int num = 0;
void run()
{
	for (int i = 0; i < N; i++)
	{
		num++;
	}
}
int main()
{
	clock_t start = clock();
	thread t1(run);
	thread t2(run);
	t1.join();
	t2.join();
	clock_t end = clock();
	cout << "num=" << num << ",用時 " << end - start << " ms" << endl;
	return 0;
}
執行結果:
num=143653419,用時 730 ms</span>
從上述程式碼執行的結果,發現結果並不是我們預計的200000000,這是由於執行緒之間發生衝突,從而導致結果不正確。 為了解決此問題,有以下方法: (1)互斥量。 例如
<span style="font-size:12px;">#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
const int N = 100000000;
int num(0);
mutex m;
void run()
{
	for (int i = 0; i < N; i++)
	{
		m.lock();
		num++;
		m.unlock();
	}
}
int main()
{
	clock_t start = clock();
	thread t1(run);
	thread t2(run);
	t1.join();
	t2.join();
	clock_t end = clock();
	cout << "num=" << num << ",用時 " << end - start << " ms" << endl;
	return 0;
}
執行結果:
num=200000000,用時 128323 ms</span>
不難發現,通過互斥量後運算結果正確,但是計算速度很慢,原因主要是互斥量加解鎖需要時間。 (2)原子變數。 例如
<span style="font-size:12px;">#include<iostream>
#include<thread>
#include<atomic>
using namespace std;
const int N = 100000000;
atomic_int num{ 0 };//不會發生執行緒衝突,執行緒安全
void run()
{
	for (int i = 0; i < N; i++)
	{
		num++;
	}
}
int main()
{
	clock_t start = clock();
	thread t1(run);
	thread t2(run);
	t1.join();
	t2.join();
	clock_t end = clock();
	cout << "num=" << num << ",用時 " << end - start << " ms" << endl;
	return 0;
}
執行結果:
num=200000000,用時 29732 ms</span>
不難發現,通過原子變數後運算結果正確,計算速度一般。
原子變數詳細內容 請參考C++11 併發之std::atomic。
(3)加入 join 。 例如
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
const int N = 100000000;
int num = 0;
void run()
{
	for (int i = 0; i < N; i++)
	{
		num++;
	}
}
int main()
{
	clock_t start = clock();
	thread t1(run);
	t1.join();
	thread t2(run);
	t2.join();
	clock_t end = clock();
	cout << "num=" << num << ",用時 " << end - start << " ms" << endl;
	return 0;
}
執行結果:
num=200000000,用時 626 ms</span>
不難發現,通過原子變數後運算結果正確,計算速度也很理想。
8、lambda與多執行緒。 例如
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
int main()
{
	auto fun = [](const char *str) {cout << str << endl; };
	thread t1(fun, "hello world!");
	thread t2(fun, "hello beijing!");
	return 0;
}
執行結果:
hello world!
hello beijing!</span>
9、時間等待相關問題。 例如:
<span style="font-size:12px;">#include<iostream>
#include<thread>
#include<chrono>
using namespace std;
int main()
{
	thread th1([]()
	{
		//讓執行緒等待3秒
		this_thread::sleep_for(chrono::seconds(3));
		//讓cpu執行其他空閒的執行緒
		this_thread::yield();
		//執行緒id
		cout << this_thread::get_id() << endl;
	});
	return 0;
}</span>
10、執行緒功能拓展。 例如:
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
class MyThread :public thread   //繼承thread
{
public:
	//子類MyThread()繼承thread()的建構函式
	MyThread() : thread()
	{
	}
	//MyThread()初始化建構函式
	template<typename T, typename...Args>
	MyThread(T&&func, Args&&...args) : thread(forward<T>(func), forward<Args>(args)...)
	{
	}
	void showcmd(const char *str)  //執行system
	{
		system(str);
	}
};
int main()
{
	MyThread th1([]()
	{
		cout << "hello" << endl;
	});
	th1.showcmd("calc"); //執行calc
	//lambda
	MyThread th2([](const char * str)
	{
		cout << "hello" << str << endl;
	}, " this is MyThread");
	th2.showcmd("notepad");//執行notepad
	return 0;
}
執行結果:
hello
//執行calc
hello this is MyThread
//執行notepad</span>
11、多執行緒可變引數。 例如:
<span style="font-size:12px;">#include<iostream>
#include<thread>
#include<cstdarg>
using namespace std;
int show(const char *fun, ...)
{
	va_list ap;//指標
	va_start(ap, fun);//開始
	vprintf(fun, ap);//呼叫
	va_end(ap);
	return 0;
}
int main()
{
	thread t1(show, "%s    %d    %c    %f", "hello world!", 100, 'A', 3.14159);
	return 0;
}
執行結果:
hello world!    100    A    3.14159</span>
12、執行緒交換。 例如:
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
int main()
{
	thread t1([]()
	{
		cout << "thread1" << endl;
	});
	thread t2([]()
	{
		cout << "thread2" << endl;
	});
	cout << "thread1' id is " << t1.get_id() << endl;
	cout << "thread2' id is " << t2.get_id() << endl;
	
	cout << "swap after:" << endl;
	swap(t1, t2);//執行緒交換
	cout << "thread1' id is " << t1.get_id() << endl;
	cout << "thread2' id is " << t2.get_id() << endl;
	return 0;
}
執行結果:
thread1
thread2
thread1' id is 4836
thread2' id is 4724
swap after:
thread1' id is 4724
thread2' id is 4836</span>
兩個執行緒通過 swap 進行交換。 13、執行緒移動。 例如:
<span style="font-size:12px;">#include<iostream>
#include<thread>
using namespace std;
int main()
{
	thread t1([]()
	{
		cout << "thread1" << endl;
	});
	cout << "thread1' id is " << t1.get_id() << endl;
	thread t2 = move(t1);;
	cout << "thread2' id is " << t2.get_id() << endl;
	return 0;
}
執行結果:
thread1
thread1' id is 5620
thread2' id is 5620</span>
從上述程式碼中,執行緒t2可以通過 move 移動 t1 來獲取 t1 的全部屬性,而 t1 卻銷燬了。

相關推薦

C++11併發std::thread

知識連結: C++11 併發之std::atomic 本文概要: 1、成員型別和成員函式。 2、std::thread 建構函式。 3、非同步。 4、多執行緒傳遞引數。 5、join、deta

c++11特性std::thread--初識二

上篇部落格《c++11特性之std::thread–初識》初步介紹了std::thread,並且介紹了幾個成員函式。 最後的一段程式碼留了點懸念,就是vs2015會報錯,錯誤如下: error

c++11特性std::thread--初識

C++11中已經擁有了一個更好用的用於執行緒操作的類std::thread。 預設建構函式: thread() noexcept; 構造一個任何執行緒不執行的執行緒物件。 初始化函式: template <class Fn, class...

[cpp].c++11學習筆記-std thread

std::thread用於啟動執行緒,可以用作跨平臺的執行緒庫。 它啟動執行緒的方式很靈活,可以支援C函式,類成員函式,類靜態函式等。 #include <thread> //標頭檔

C++11 併發指南二(std::thread 詳解)

上一篇部落格《C++11 併發指南一(C++11 多執行緒初探)》中只是提到了 std::thread 的基本用法,並給出了一個最簡單的例子,本文將稍微詳細地介紹 std::thread 的用法。 std::thread 在 <thread> 標頭檔案中宣告,因此使用 std::thread 時

基於C++11併發庫的執行緒池與訊息佇列多執行緒框架——std::thread

1 前言  C++11標準在標準庫中為多執行緒提供了元件,這意味著使用C++編寫與平臺無關的多執行緒程式成為可能,而C++程式的可移植性也得到了有力的保證。    在之前我們主要使用的多執行緒庫要麼是屬於某個單獨平臺的,例如:POSIX執行緒庫(Linux),Windows

C++多執行緒std::thread

C++11,包含標頭檔案 thread.h,並使用名稱空間std。 thread類提供的方法 方法 描述 thread 建構函式,在這裡傳入執行緒執行函式,和函式引數

C++11併發學習三:執行緒同步

1.<mutex> 標頭檔案介紹  (1)Mutex系列類(四種) std::mutex,最基本的 Mutex 類。 std::recursive_mutex,遞迴 Mutex 類。 std::time_mutex,定時 Mutex 類。 std::recursive_ti

C++11併發學習四:執行緒同步(續)

有時候,在第一個執行緒完成前,可能需要等待另一個執行緒執行完成。C++標準庫提供了一些工具可用於這種同步操作,形式上表現為條件變數(condition variable)和期望(future)。 一.條件變數(condition variable) C++標準庫對條件變數有兩套實現:std::c

C++11併發學習二:執行緒管理

1.啟動執行緒 (1)使用物件 “小試牛刀”中thread構造時傳入的是函式,還可以傳入物件。 #include <thread> #include <iostream>   void func() {     std::cout<<

C++11 併發指南六( 型別詳解二 std::atomic )

C++11 併發指南六(atomic 型別詳解一 atomic_flag 介紹)  一文介紹了 C++11 中最簡單的原子型別 std::atomic_flag,但是 std::atomic_flag 過於簡單,只提供了 test_and_set 和 clear 兩個 API,不能滿足其他需求(如 s

C++11 併發指南三(std::mutex 詳解)

上一篇《C++11 併發指南二(std::thread 詳解)》中主要講到了 std::thread 的一些用法,並給出了兩個小例子,本文將介紹 std::mutex 的用法。 Mutex 又稱互斥量,C++ 11中與 Mutex 相關的類(包括鎖型別)和函式都宣告在 <mutex> 標頭檔案中

C++11 併發指南五(std::condition_variable 詳解)

前面三講《C++11 併發指南二(std::thread 詳解)》,《C++11 併發指南三(std::mutex 詳解)》分別介紹了 std::thread,std::mutex,std::future 等相關內容,相信讀者對 C++11 中的多執行緒程式設計有了一個最基本的認識,本文將介紹 C++11 標

C++11 併發指南六( 型別詳解二 std::atomic )

C++11 併發指南六(atomic 型別詳解一 atomic_flag 介紹)  一文介紹了 C++11 中最簡單的原子型別 std::atomic_flag,但是 std::atomic_flag 過於簡單,只提供了 test_and_set 和 clear 兩個 API,不能滿足其他需求(如 store

C++11 併發指南四( 詳解三 std::future & std::shared_future)

上一講《C++11 併發指南四(<future> 詳解二 std::packaged_task 介紹)》主要介紹了 <future> 標頭檔案中的 std::packaged_task 類,本文主要介紹 std::future,std::shared_future 以及 std::fu

C++11 併發指南六(atomic 型別詳解三 std::atomic (續))

integral fetch_add(integral, memory_order = memory_order_seq_cst) volatile; integral fetch_add(integral, memory_order = memory_order_seq_cst); integral

C++11 併發指南四( 詳解一 std::promise 介紹)

前面兩講《C++11 併發指南二(std::thread 詳解)》,《C++11 併發指南三(std::mutex 詳解)》分別介紹了 std::thread 和 std::mutex,相信讀者對 C++11 中的多執行緒程式設計有了一個最基本的認識,本文將介紹 C++11 標準中 <future>

C++11 併發指南四( 詳解二 std::packaged_task 介紹)

上一講《C++11 併發指南四(<future> 詳解一 std::promise 介紹)》主要介紹了 <future> 標頭檔案中的 std::promise 類,本文主要介紹 std::packaged_task。 std::packaged_task 包裝一個可呼叫的物件,並且

C++11 併發指南四( 詳解一 std::promise 介紹)

前面兩講《C++11 併發指南二(std::thread 詳解)》,《C++11 併發指南三(std::mutex 詳解)》分別介紹了 std::thread 和 std::mutex,相信讀者對 C++11 中的多執行緒程式設計有了一個最基本的認識,本文將介紹 C++

C++11 併發指南四( 詳解二 std::packaged_task 介紹)

上一講《C++11 併發指南四(<future> 詳解一 std::promise 介紹)》主要介紹了 <future> 標頭檔案中的 std::promise 類,本文主要介紹 std::packaged_task。 std::package