C++多线程快速入门(一):基本常用操作
目錄
- 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 0case2:創建線程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 0case3:創建線程 線程傳參 functional object作為參數
關于仿函數的定義可以看:仿函數
如果單純地將邏輯函數傳入thread,在函數邏輯比較復雜的時候不太好。
將函數封裝到類的內部,可以賦予一定的封裝性,并且可以在類內部創建例如map的數據結構來記錄函數運行的狀態。
仿函數、不帶參
打印結果如下:
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 并不是線程安全的,所以可以發現,有亂碼的樣子
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 0case4:觀察多線程程序加速計算
#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>
運行結果如下:
C:\Users\LENOVO\CLionProjects\untitled\cmake-build-debug\untitled.exe ans = 887459712 執行了0.0521455秒 ans = 887459712 執行了0.250044秒Process finished with exit code 0case6:互斥鎖
多個線程訪問同一個值并且對其修改,會導致數據競爭。:
#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 0case7:std::lock_guard 類模板
如果mtx.lock()和mtx.unlock()之間的語句發生了異常,unlock()語句沒有機會執行!
這會導致mtx一直處于鎖著的狀態,其他使用sum函數的線程就會阻塞。
c++庫已經提供了std::lock_guard類模板,構造時自動加鎖,析構時自動解鎖:
將case6的sum函數修改為下面形式即可:
如果想自己實現的lock_guard的話也可以:
class mutexLockGuard{ private:std::mutex& mtx; public:explicit mutexLockGuard(std::mutex& mutex) : mtx(mutex){mtx.lock();}~mutexLockGuard() {mtx.unlock();} };筆記參考:
C++多線程快速入門
總結
以上是生活随笔為你收集整理的C++多线程快速入门(一):基本常用操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CMake学习使用(基于vscode)
- 下一篇: C++多线程快速入门(二)共享数据同步以