目錄
一、線程庫thread
1.使用外部函數
2. 使用類的函數
3. 添加參數
二、線程庫 mutex
1.使用lock()方法
2.try_lock()方法
三、線程庫lock_guard
四、線程庫unique_lock
1.adopt_lock
2.defer_lock()
五、線程庫call_once
六、線程庫promise & future
七、condition變量使用場景
八、async 和 packaged_task
1.async
2.packaged_task
一、線程庫thread
1.使用外部函數
????????下面這段代碼的目的是等待子線程運行結束,因為可能存在主線程已經結束了,但是子線程還有程序要運行,此時直接return可能會出問題。
t1.join();
2. 使用類的函數
#include<iostream>
#include<chrono>
#include<thread>
using namespace std;int i = 0;
void test1()
{while(i< 10){cout << "子線程運行中:" << i << endl;i++;this_thread::sleep_for(chrono::microseconds(10));}
}
class A
{private:int i = 0;public:void test2(){while(i< 10){cout << "子線程運行中:" << i << endl;i++;this_thread::sleep_for(chrono::microseconds(10));}}
};int main()
{A a;thread t1(&A::test2, &a);while(i< 10){cout << "主線程運行中:" << i << endl;i++;this_thread::sleep_for(chrono::microseconds(10));}t1.join();return 0;
}
3. 添加參數
#include<iostream>
#include<chrono>
#include<thread>
using namespace std;
int i = 0;
class A
{private:int i = 0;public:void test2(int num){while(i< 10){cout << "子線程運行中:num:" << num << endl;this_thread::sleep_for(chrono::microseconds(10));i++;}}
};int main()
{A a;thread t1(&A::test2, &a, 100);while(i< 10){cout << "主線程運行中:" << i << endl;i++;this_thread::sleep_for(chrono::microseconds(10));}t1.join();return 0;
}
二、線程庫 mutex
對于上面第一個的例子,存在主線程或者子線程打印一半,時間片結束,進入另外一個線程打印,導致亂碼,為了解決這個問題,引入鎖。
1.使用lock()方法
使用lock加鎖之后,當子線程執行一般,此時還沒釋放鎖,進入主線程,主線程也調用lock(),發現鎖已經被占用了,于是就等待,之后時間片到了進入子線程,子線程程序執行完畢后釋放鎖unlock()。
2.try_lock()方法
這個方法嘗試獲取鎖,成功獲得返回true,否則返回false,他不會阻塞,獲取不到鎖也可以做一些其他操作。
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
using namespace std;
int i = 0;
mutex mtx;
void test2()
{while(i< 10){if(mtx.try_lock()){cout << "子線程運行中:" << i << endl;this_thread::sleep_for(chrono::microseconds(10000));i++;mtx.unlock();}else{cout << "子線程等待中:"<< endl;}}
}
int main()
{thread t1(test2);while(i< 10){if(mtx.try_lock()){cout << "主線程運行中:" << i << endl;i++;this_thread::sleep_for(chrono::microseconds(10000));mtx.unlock();}else{cout << "主線程等待中:"<< endl;}}t1.join();return 0;
}
三、線程庫lock_guard
上面每次都需要手動釋放,可能存在忘了釋放鎖,就會有bug,下面解決這個問題。本質上當這個變量作用域結束的時候執行析構函數,自動釋放鎖。
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
#include<sstream>
using namespace std;int i = 0;
mutex mtx;// 輔助函數,用于獲取線程 ID 字符串
string getThreadId() {stringstream ss;ss << this_thread::get_id();return ss.str();
}void test2()
{while(i < 10){lock_guard<mutex> lock(mtx);cout << "子線程 " << getThreadId() << " 運行中:" << i << endl;this_thread::sleep_for(chrono::microseconds(10));i++;}
}int main()
{thread t1(test2);while(i < 10){lock_guard<mutex> lock(mtx);cout << "主線程 " << getThreadId() << " 運行中:" << i << endl;i++;this_thread::sleep_for(chrono::microseconds(10));}t1.join();return 0;
}
四、線程庫unique_lock
unique_lock提供了更高級的用法。它可以多傳一個參數,也可以不傳,不穿的用法和lock_guard一樣。
1.adopt_lock
它的作用是接管鎖,上面先創建一個鎖,之后由他接管,作用域結束的時候自動釋放。
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
#include<sstream>
using namespace std;int i = 0;
mutex mtx;// 輔助函數,用于獲取線程 ID 字符串
string getThreadId() {stringstream ss;ss << this_thread::get_id();return ss.str();
}void test2()
{while(i < 10){mtx.lock();unique_lock<mutex> lock(mtx, adopt_lock);cout << "子線程 " << getThreadId() << " 運行中:" << i << endl;this_thread::sleep_for(chrono::microseconds(10));i++;}
}int main()
{thread t1(test2);while(i < 10){mtx.lock();unique_lock<mutex> lock(mtx, adopt_lock);cout << "主線程 " << getThreadId() << " 運行中:" << i << endl;i++;this_thread::sleep_for(chrono::microseconds(10));}t1.join();return 0;
}
2.defer_lock()
defer_lock的作用是延遲鎖,在后面調用lock.lock();才會獲取鎖和加鎖。
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
#include<sstream>
using namespace std;int i = 0;
mutex mtx;// 輔助函數,用于獲取線程 ID 字符串
string getThreadId() {stringstream ss;ss << this_thread::get_id();return ss.str();
}void test2()
{while(i < 10){unique_lock<mutex> lock(mtx, defer_lock);lock.lock();cout << "子線程 " << getThreadId() << " 運行中:" << i << endl;this_thread::sleep_for(chrono::microseconds(10));i++;}
}int main()
{thread t1(test2);while(i < 10){unique_lock<mutex> lock(mtx, defer_lock);lock.lock();cout << "主線程 " << getThreadId() << " 運行中:" << i << endl;i++;this_thread::sleep_for(chrono::microseconds(10));}t1.join();return 0;
}
五、線程庫call_once
????????如果存在好幾個線程,調用一個函數,這個函數里面有個初始化操作,這個操作只需要被執行一次,就會用到下面的操作。
? ? ? ? 聲明once_flag, 之后將once_flag,init函數和以及init函數的參數傳遞給call_once。
六、線程庫promise & future
這個主要作用是異步獲取線程函數里面的數據。int sum = ft.get();這段代碼會阻塞,直到子線程設置ps.set_value(sum);。注意使用future<int> ft = ps.get_future();聲明的話,主線程只能get一次,如果想要get多次,那么使用shared_future<int> ft = ps.get_future();。
七、condition變量使用場景
? ? ? ? 生產者消費者模型,生產者不斷往隊列中添加任務,之后通知消費者取任務。
關鍵代碼解釋
cv.notify_one();? ?// 通知另外一個線程取任務
cv.wait(lck, []{return !q.empty();});? ? //? 這個鎖的參數很有必要,因為上面給鎖住了,所以需要先釋放了鎖才行,所以鎖也需要作為參數傳入,后面的就是條件,如果為True就等待。
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
#include<queue>
#include<condition_variable>
using namespace std;queue<int> q;
condition_variable cv;
mutex mtx;
void Product()
{for(int i=0; i<10; i++){{unique_lock<mutex> lck(mtx);q.push(i);cv.notify_one();cout << "生產了" << i << endl << flush; }this_thread::sleep_for(chrono::milliseconds(5)); // 延長時間}
}void Consumer()
{while(true){unique_lock<mutex> lck(mtx);cv.wait(lck, []{return !q.empty();});int data = q.front();q.pop();cout << "消費了" << data << ",隊列大小: " << q.size() << endl << flush;if(data == -1) break; // 正確退出this_thread::sleep_for(chrono::milliseconds(3)); }
}
void test2()
{ thread t2(Consumer);thread t1(Product);t1.join();// 添加退出條件,避免消費者線程無限循環{unique_lock<mutex> lck(mtx);q.push(-1); // 發送結束信號cv.notify_one();}t2.join();
}int main()
{test2();return 0;
}
八、async 和 packaged_task
他倆的作用都是異步獲取函數返回值。
1.async
關鍵代碼解釋:
future<int> f = async(add);? ? // 自動產生一個線程執行add這個函數
f.get()? // 讀取函數的返回值
2.packaged_task
關鍵代碼解釋:
packaged_task<int()> task(add);? ? ? // 創建一個packaged_task對象,不會創建線程
auto future_result = task.get_future();? ? // 得到這個任務的future對象
thread t(move(task));? ? ? // 根據任務創建線程,開始這行任務里面的函數
cout<<"sum = "<<future_result.get()<<endl;? ?// 等待函數執行完成,獲得返回值,對于future的get方法,如果函數沒有執行完成,就會阻塞。