目錄
- case1:創建線程1 join、detach
- case2:創建線程2 線程傳參 傳值或者傳引用
- case3:創建線程 線程傳參 functional object作為參數
- case4:觀察多線程程序加速計算
- case5:future + get 獲取并發結果
- case6:互斥鎖
- case7:std::lock_guard 類模板
case1:創建線程1 join、detach
創建線程,并等該線程執行完畢,并且打印兩個線程的id
#include <iostream>
#include <thread>
using namespace std;
void func() {cout << "hello , this is my thread, thread id is " << this_thread::get_id() << endl;
}
int main() {thread th = thread(func);th.join();cout << "this is main thread and its id is " << this_thread::get_id() << endl;
}
執行結果如下:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
hello , this is my thread, thread id is 2
this is main thread and its id is 1Process finished with exit code 0
使用detach,放棄對該線程的控制:
#include <iostream>
#include <thread>using namespace std;void func() {cout << "hello , this is my thread, thread id is " << this_thread::get_id() << endl;
}
int main() {thread th = thread(func);th.detach();// 如果我們此時不再關系該線程的運行情況的話可以使用detachcout << th.joinable() << endl;cout << "this is main thread and its id is " << this_thread::get_id() << endl;
}
運行結果:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
0
this is main thread and its id is 1
hello , this is my thread, thread id is 2Process finished with exit code 0
case2:創建線程2 線程傳參 傳值或者傳引用
傳值
#include <iostream>
#include <thread>using namespace std;void func(string s) {cout << "hello , this is my thread, thread arg is " << s << endl;
}
int main() {thread th = thread(func, "test");th.join();cout << "this is main thread and its id is " << this_thread::get_id() << endl;
}
打印結果如下:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
hello , this is my thread, thread arg is test
this is main thread and its id is 1Process finished with exit code 0
傳引用
#include <iostream>
#include <thread>using namespace std;void func(string& s) {cout << (&s) << endl;cout << "hello , this is my thread, thread arg is " << s << endl;
}
int main() {string str = "test";thread th = thread(func, ref(str));cout << (&str) << endl;th.join();cout << "this is main thread and its id is " << this_thread::get_id() << endl;
}
打印結果如下:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
0x62fd10
0x62fd10
hello , this is my thread, thread arg is test
this is main thread and its id is 1Process finished with exit code 0
case3:創建線程 線程傳參 functional object作為參數
關于仿函數的定義可以看:仿函數
如果單純地將邏輯函數傳入thread,在函數邏輯比較復雜的時候不太好。
將函數封裝到類的內部,可以賦予一定的封裝性,并且可以在類內部創建例如map的數據結構來記錄函數運行的狀態。
仿函數、不帶參
#include <iostream>
#include <thread>using namespace std;// functional object
struct A {void operator()() {cout << "I'm A" << endl;}
};
void show() {cout << "I'm show" << endl;
}
int main() {show();A a;a(); // 等價于 a.operator()();// 我們把這種object稱為callable的object ,又稱為仿函數thread thread1 = thread(A());thread1.join();return 0;
}
打印結果如下:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
I'm show
I'm A
I'm AProcess finished with exit code 0
仿函數、帶參
#include <iostream>
#include <thread>using namespace std;// functional object
struct A {void operator()(int num) {for (int i = 0; i < num; i++) {cout << "I'm A" << i << endl;}}
};int main() {int num = 10;thread thread1 = thread(A(), num);for (int i = 0; i < num; i++) {cout << "I'm main" << i << endl;}thread1.join();return 0;
}
打印結果如下,是亂序的,符合多線程特性
并且 cout 并不是線程安全的,所以可以發現,有亂碼的樣子
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
I'm mainI'm A00I'm A1
I'm A2
I'm A3
I'm AI'm main41I'm mainI'm A25I'm mainI'm A36I'm AI'm main47I'm main5
I'm mainI'm A86I'm mainI'm A97I'm main8
I'm main9Process finished with exit code 0
lambda函數作為參數
#include <iostream>
#include <thread>using namespace std;int main() {string s = "test";thread f = thread([&s](int a,int b) {cout << s << endl;cout << a + b << endl;}, 2, 3);f.join();return 0;
}
打印結果:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
test
5Process finished with exit code 0
case4:觀察多線程程序加速計算
#include <iostream>
#include <thread>using namespace std;// 測量一個函數的運行時間
template <class T>
void measure(T&& func) {using namespace std::chrono;auto start = system_clock::now();// funcfunc();duration<double> diff = system_clock::now() - start;cout << "執行了" << diff.count() << "秒" << endl;
}// 求和函數[start,end)
void sum(long start, long end, long& ans) {long s = 0;for(auto i = start; i < end; i++) {s += i;}ans = s;
}const long S = 100000000;int main() {// 測量一下把工作分攤給兩個線程做的時間measure([](){long ans1, ans2;thread t1 = thread(sum, 0, S >> 1, std::ref(ans1));thread t2 = thread(sum, S >> 1, S, std::ref(ans2));t1.join();t2.join();cout << "ans = " << ans1 + ans2 << endl;});// 測量一下單線程工作時間measure([](){long ans;sum(0, S, ans);cout << "ans = " << ans << endl;});return 0;
}
打印結果:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
ans = 887459712
執行了0.13546秒
ans = 887459712
執行了0.240006秒Process finished with exit code 0
當然這里有一個細節:
如果在多線程程序中我們傳入了一個ref引用,在線程程序中我們最好不要直接對這個ref值操作,而是采用一個臨時變量,例如上面的程序中我們就使用了一個臨時變量s,對s進行累加后再把值賦給ans。這樣會提高程序運行效率。
case5:future + get 獲取并發結果
future包裹線程運行結果,使用方法如下:
由于我們線程函數的結果是long,所以首先定義future<long>
#include <iostream>
#include <thread>
#include <future>
#include <vector>using namespace std;// 測量一個函數的運行時間
template <class T>
void measure(T&& func) {using namespace std::chrono;auto start = system_clock::now();// funcfunc();duration<double> diff = system_clock::now() - start;cout << "執行了" << diff.count() << "秒" << endl;
}// 求和函數[start,end)
long sum(long start, long end) {long s = 0;for(auto i = start; i < end; i++) {s += i;}return s;
}const long S = 100000000;int main() {// 測量一下把工作分攤給 threadNums 個線程做的時間measure([](){const long threadNums = 8;vector<future<long>> vec;vec.reserve(threadNums);for (int i = 0; i < threadNums; i++) {vec.push_back(async(sum, (S / threadNums) * i, (S / threadNums) * (i + 1)));}long ans = 0;// get 阻塞式地拿到并發結果for (int i = 0; i < threadNums; i++) {ans += vec[i].get();}cout << "ans = " << ans << endl;});// 測量一下單線程工作時間measure([](){long ans = sum(0, S);cout << "ans = " << ans << endl;});return 0;
}
運行結果如下:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
ans = 887459712
執行了0.0521455秒
ans = 887459712
執行了0.250044秒Process finished with exit code 0
case6:互斥鎖
多個線程訪問同一個值并且對其修改,會導致數據競爭。:
#include <iostream>
#include <thread>
#include <future>
#include <vector>using namespace std;
// 測量一個函數的運行時間
template <class T>
void measure(T&& func) {using namespace std::chrono;auto start = system_clock::now();// funcfunc();duration<double> diff = system_clock::now() - start;cout << "執行了" << diff.count() << "秒" << endl;
}std::mutex mtx;
// 求和函數 線程安全的
void sum(long& s) {mtx.lock();for (int i = 0; i < 100000; i++) {s++;}mtx.unlock();
}int main() {// 測量一下把工作分攤給 threadNums 個線程做的時間measure([](){vector<thread> v;long s = 0;for (int i = 0; i < 4; i++) {v.emplace_back(std::thread(sum, std::ref(s)));}for (int i = 0; i < 4; i++) {v[i].join();}cout << "ans " << s << endl;});measure([](){long s = 0;for (int i = 0; i < 4; i++) {sum(s);}cout << "ans " << s << endl;});return 0;
}
測試結果:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe
ans 400000
執行了0.0141654秒
ans 400000
執行了0.0155926秒Process finished with exit code 0
case7:std::lock_guard 類模板
如果mtx.lock()和mtx.unlock()之間的語句發生了異常,unlock()語句沒有機會執行!
這會導致mtx一直處于鎖著的狀態,其他使用sum函數的線程就會阻塞。
c++庫已經提供了std::lock_guard類模板,構造時自動加鎖,析構時自動解鎖:
將case6的sum函數修改為下面形式即可:
// 求和函數 線程安全的
void sum(long& s) {std:;lock_guard<std::mutex> guard(mtx);// mtx.lock();for (int i = 0; i < 100000; i++) {s++;}// mtx.unlock();
}
如果想自己實現的lock_guard的話也可以:
class mutexLockGuard{
private:std::mutex& mtx;
public:explicit mutexLockGuard(std::mutex& mutex) : mtx(mutex){mtx.lock();}~mutexLockGuard() {mtx.unlock();}
};
筆記參考:
C++多線程快速入門