1 unique_lock
unique_lock可完成lock_guard的功能,另外還有額外的參數,實現其它功能
unique_lock的defer_lock參數,即先不加鎖,只是先綁定unique_lock與mutex,另外,可以 隨時進行加鎖、解鎖操作,某些情況下可提高效率(注意此時的加、解鎖是通過unique_lock的成員函數.lock() 與 .unlock()實現的)
unique_lock還可以 交換管理權(unique_lock可以被移動,lock_guard不可以)
代碼示例:
#include #include #include #include #include class LogFile { std::mutex m_mutex;//鎖 std::ofstream f; public: LogFile() { f.open("log.txt"); } void shared_print(std::string id, int value) { std::unique_lock locker(m_mutex,std::defer_lock);//先不加鎖 //...此處代碼不需要保護 locker.lock(); f << id << ":" << value << std::endl;//此處才是保護區 std::cout << id << ":" << value << std::endl; locker.unlock(); //...此處代碼也不需要保護 locker.lock(); //以下可能又需要保護了 //unique_lock可以被移動,lock_guard不可以 std::unique_lock locker2 = std::move(locker); //將unique_lock管理的lock交給新的unique_lock的管理 //即將m_mutex與locker2綁定在一起!此時locker已無用 } }; void function_1(LogFile& log) { for (int i = 0; i > -1000; i--) log.shared_print(std::string("from t1:"), i); } int main()//主線程 { LogFile log; std::thread t1(function_1, std::ref(log));//t1線程開始運行 for (int i = 0; i < 1000; i++) { log.shared_print(std::string("from main:"), i); } t1.join(); return 0; }
程序運行結果依然是主線程和子線程各自輸出1000條信息以及將信息保存到txt文件中,和上篇中 “死鎖 & adopt_lock” 的結果類似,這里不再展示。
2 call_once
之前測試代碼中,log.txt文件的創建(f.open("log.txt");)是在類的構造函數中的,如果沒有使用到打印函數(shared_print()),則沒必要創建該文件,若將其放入打印函數中,會被多次調用。
這時,可以使用call_once()函數并配合once_flag標記,保證即使是多線程情況下,也只被執行一次。
代碼:

#include #include #include #include #include class LogFile { std::mutex m_mutex; std::once_flag m_flag;//once_flag標記 std::ofstream f; public: LogFile() { //f.open("log.txt");//不再需要! } //此種情況是:那個線程先要打印,則首次創建文件(都不需打印,則始終不創建文件) void shared_print(std::string id, int value) { std::call_once(m_flag, [&]() {f.open("log.txt"); });//call_once函數 std::unique_lock locker(m_mutex); f << id << ":" << value << std::endl; std::cout << id << ":" << value << std::endl; } }; void function_1(LogFile& log) { for (int i = 0; i > -1000; i--) log.shared_print(std::string("from t1:"), i); } int main()//主線程 { LogFile log; std::thread t1(function_1, std::ref(log));//t1線程開始運行 for (int i = 0; i < 1000; i++) { log.shared_print(std::string("from main:"), i); } t1.join(); return 0; }
程序運行結果同上,不再展示。
3 condition_varible & wait
首先引入一個“ 生產者消費者問題 ”,有兩個線程,線程A產生數據(生產者),線程B獲取數據(消費者),線程A在產生數據時,線程B不能過來取,因而線程B的執行必須要依賴線程A。
下面的程序通過對隊列結構中存取和取出數據,模擬生產和消費。采用加鎖機制,保證兩個子線程互斥,并且消費者線程使用循環查詢機制,不斷檢查是否有可用數據。
代碼示例:
#include #include #include #include #include #include #include #include using namespace std; deque q;//整形隊列(共享內存) mutex mu;//互斥對象 void function_1()//生產者 { int count = 10; while (count > 0) { unique_lock locker(mu); q.push_front(count);//放入一個數 locker.unlock(); this_thread::sleep_for(chrono::seconds(1)); count--; } } void function_2()//消費者 { int data = 0; while (data != 1) { unique_lock locker(mu); if (!q.empty()) { data = q.back(); q.pop_back();//取出一個數 locker.unlock(); cout << "t2 got a value from t1:" << data << endl; } else//隊列為空時,等待一段時間再繼續,減少無意義的判斷次數 { locker.unlock(); this_thread::sleep_for(chrono::seconds(5));//不加此句,function_2一直循環檢測q是否為空 } cout << "function_2..." << endl; } } int main()//主線程 { thread t1(function_1); thread t2(function_2); t1.join(); t2.join(); return 0; }
運行結果:
t2 got a value from t1:10 function_2... function_2... t2 got a value from t1:9 function_2... t2 got a value from t1:8 function_2... t2 got a value from t1:7 function_2... t2 got a value from t1:6 function_2... function_2... t2 got a value from t1:5 function_2... t2 got a value from t1:4 function_2... t2 got a value from t1:3 function_2... t2 got a value from t1:2 function_2... t2 got a value from t1:1 function_2... 請按任意鍵繼續. . .
程序輸出的中間過程會有些停頓,且消費者線程(function_2)存在多次查詢無果的情況,注意上面程序的function_2中還特別加入了線程休眠等待(sleep_for),如果不加,function_2會不停的進行無效的查詢訪問,效率極低,而加入的等待時間過程,又會使function_2不能及時獲取數據。
針對上面的問題,就需要引入 條件變量 condition_varible ,配合.wait()與.notify_one()成員函數,即可通過“ 等待通知 ”的形式使function_2在恰當的時間獲得數據,避免無謂低效的查詢。
修改后程序如下(頭文件包含以及主函數省略,與上同):
deque q;//整形隊列 mutex mu;//互斥對象->鎖 condition_variable cond;//【條件變量】避免線程無謂的循環 void function_1() { int count = 10; while (count > 0) { unique_lock locker1(mu);//使用unique_lock來管理鎖 q.push_front(count);//隊列中寫數據(隊前插入)10 9 8...1 0 locker1.unlock();//提前解鎖 //cond.notify_one();//激活一個等待這個條件的線程 cond.notify_all();//激活所有類似線程2的等待線程 this_thread::sleep_for(chrono::seconds(1));//此線程休息1秒 count--; } } void function_2() { int data = 0; while (data != 1) { unique_lock locker2(mu);//互斥對象mu被線程2鎖住,線程不會在鎖住的情況下休眠 cond.wait(locker2, []() {return !q.empty(); });//將線程2休眠直到線程1的notify_one()才將其激活 //上句第一個參數是解鎖加鎖,第2個參數為【lambda函數】,防止偽激活 data = q.back();//隊列中讀數據 q.pop_back();//隊列中刪除數據(隊尾刪除) locker2.unlock();//提前解鎖 cout << "t2 got a value from t1:" << data << endl; } }
運行結果:
t2 got a value from t1:10 t2 got a value from t1:9 t2 got a value from t1:8 t2 got a value from t1:7 t2 got a value from t1:6 t2 got a value from t1:5 t2 got a value from t1:4 t2 got a value from t1:3 t2 got a value from t1:2 t2 got a value from t1:1 請按任意鍵繼續. . .
此時function_2就可以在functon_1產生數據后及時獲取了,并且沒有了無效的查詢過程。
4 future & async
std::async為一函數模板,用來啟動一 異步任務(即自動創建一線程并執行對應的線程入口函數),之后返回一個std::future對象(對象中包含線程函數的返回結果),最后通過future對象的.get()成員函數來獲取結果。 (如果只是簡單地通過引用的方式在子線程和主線程間傳遞結果,需要 額外的加鎖 機制!)
.get()成員函數等待子線程返回結果,否則一直等待(注:只能get一次,多次調用則報異常) 與之類似的.wait()成員函數只等待結果,不獲取結果(類似于join())
如果.get()和 .wait()都不用,主程序結束時仍等待子線程
future:理解為提供了一種訪問異步操作結果的機制,即需要等待一段時間(線程執行完畢) 主線程才能從 子線程 中拿到結果
額外向async()傳遞一個參數(std::launch枚舉類型),實現其它功能
std::launch::deffered:表示線程入口函數被延遲到get()或wait()時才執行(但仍是主線程,沒有新線程!),若沒有get()或wait()則始終不執行子線程
std::launch::async:立即創建一個新線程
std::launch::async|std::launch::deffered:根據系統資源消耗情況,可能立即創建新線程,也可能延遲
代碼示例:
#include #include #include #include #include #include//引入future頭文件 using namespace std; int factorial(int N)//階乘函數 { cout << "子線程啟動>>>>> 線程ID: " << this_thread::get_id() << endl; int res = 1; for (int i = N; i > 1; i--) res *= i; chrono::milliseconds dura(3000);//定義一個3秒的時間 this_thread::sleep_for(dura);//模擬長時間計算 //cout << "Result is:" << res << endl; cout << "子線程結束<<<<< 線程ID: " << this_thread::get_id() << endl; return res; } int main()//主線程 { cout << "主線程啟動>>>>>>>>>>>>> 線程ID: " << this_thread::get_id() << endl; int x = 0; //future fu = async(launch::async | launch::deferred, factorial, 4);//【兩者方式均可能】 future fu = async(launch::async, factorial, 4);//【強制創建一個線程】 //future fu = async(launch::deferred, factorial, 4);//【延時執行】 //future fu = async(factorial, 4);//【不加參數等價于async|deferred】 cout << "continue..." << endl; cout << "continue..." << endl; cout << "continue..." << endl; x = fu.get();//等待子線程結果 cout << "Result is:" << x << endl; cout << "主線程啟動<<<<<<<<<<<<< 線程ID: " << this_thread::get_id() << endl; return 0; }
運行結果:
主線程啟動>>>>>>>>>>>>> 線程ID: 2468 continue...子線程啟動>>>>> 線程ID: 9980 continue... continue... 子線程結束<<<<< 線程ID: 9980 Result is:24 主線程啟動<<<<<<<<<<<<< 線程ID: 2468 請按任意鍵繼續. . .
5 promise
std::promise類模板(事先約定)可以在某個線程中給它賦值,然后在其它線程中,將該值取出
代碼示例:
#include #include #include #include #include #include using namespace std; int factorial(future&f)//階乘函數 { int res = 1; int N = f.get(); for (int i = N; i > 1; i--) res *= i; cout << "Result is:" << res << endl; return res; } int main()//主線程 { int x; promise p;//主線程中的int變量(“約定型”變量) future f = p.get_future();//該變量值的值約定從“將來”future獲得 cout << "pass the promise-future 'p' to factorial()..." << endl; future fu = async(launch::async, factorial, ref(f));//按引用傳遞f(一個未來的約定) this_thread::sleep_for(chrono::seconds(3));//此線程休息3秒(模擬未來的時間) p.set_value(4);//(約定時間到)為其賦值,此時子線程factorial才能獲得參數值 x = fu.get();//獲得子線程factorial的計算結果 cout << "Get from child thread:" << x << endl; return 0; }
運行結果:
pass the promise-future 'p' to factorial()... Result is:24 Get from child thread:24 請按任意鍵繼續. . .
C++ 任務調度 多線程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。