c++11新特性多線程操作實(shí)戰(zhàn)
c++11多線程操作
線程
thread
int main() { thread t1(Test1); t1.join(); thread t2(Test2); t2.join(); thread t3 = t1; thread t4(t1); thread t5 = std::move(t1); thread t6(std::move(t1)); return 0; }
t3,t4創(chuàng)建失敗,因?yàn)閠hread的拷貝構(gòu)造和賦值運(yùn)算符重載的原型是:
thread(const thread&) = delete; thread& operator=(const thread&) = delete;
被禁用了,但是t5, t6線程是創(chuàng)建成功的。std::move把t1轉(zhuǎn)換為右值,調(diào)用的是函數(shù)原型為thread& operator=(thread&& _Other) noexcept
和thread(thread&& _Other) noexcept
。
當(dāng)線程對(duì)象t1被移動(dòng)拷貝和移動(dòng)賦值給t5和t6的時(shí)候,t1就失去了線程控制權(quán),也就是一個(gè)線程只能同時(shí)被一個(gè)線程對(duì)象所控制。最直觀的是t1.joinable()返回值為false,joinable()函數(shù)后面介紹。
使用類成員函數(shù)作為線程參數(shù):
class Task { public: Task(){} void Task1() {} void Task2() {} private: }; int main() { Task task; thread t3(&Task::Task1, &task); t3.join(); return 0; }
關(guān)鍵點(diǎn)是要?jiǎng)?chuàng)建一個(gè)類對(duì)象,并作為第二個(gè)參數(shù)傳入thread()線程的構(gòu)造函數(shù)中去。
管理當(dāng)前線程的函數(shù)
yield
此函數(shù)的準(zhǔn)確性為依賴于實(shí)現(xiàn),特別是使用中的 OS 調(diào)度器機(jī)制和系統(tǒng)狀態(tài)。例如,先進(jìn)先出實(shí)時(shí)調(diào)度器( Linux 的 SCHED_FIFO )將懸掛當(dāng)前線程并將它放到準(zhǔn)備運(yùn)行的同優(yōu)先級(jí)線程的隊(duì)列尾(而若無其他線程在同優(yōu)先級(jí),則 yield 無效果)。
#include <iostream> #include <chrono> #include <thread> // 建議其他線程運(yùn)行一小段時(shí)間的“忙睡眠” void little_sleep(std::chrono::microseconds us) { auto start = std::chrono::high_resolution_clock::now(); auto end = start + us; do { std::this_thread::yield(); } while (std::chrono::high_resolution_clock::now() < end); } int main() { auto start = std::chrono::high_resolution_clock::now(); little_sleep(std::chrono::microseconds(100)); auto elapsed = std::chrono::high_resolution_clock::now() - start; std::cout << "waited for " << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count() << " microseconds\n"; }
get_id
這個(gè)函數(shù)不用過多介紹了,就是用來獲取當(dāng)前線程id的,用來標(biāo)識(shí)線程的身份。
std::thread::id this_id = std::this_thread::get_id();
sleep_for
位于this_thread命名空間下,msvc下支持兩種時(shí)間參數(shù)。
std::this_thread::sleep_for(2s); std::this_thread::sleep_for(std::chrono::seconds(1));
sleep_untile
參數(shù)構(gòu)建起來挺麻煩的,一般場(chǎng)景下要求線程睡眠的就用sleep_for就行了
using std::chrono::system_clock; time_t tt = system_clock::to_time_t(system_clock::now()); struct std::tm *ptm = localtime(&tt); std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
互斥
mutex
對(duì)于互斥量看到一個(gè)很好的比喻:
單位上有一臺(tái)打印機(jī)(共享數(shù)據(jù)a),你要用打印機(jī)(線程1要操作數(shù)據(jù)a),同事老王也要用打印機(jī)(線程2也要操作數(shù)據(jù)a),但是打印機(jī)同一時(shí)間只能給一個(gè)人用,此時(shí),規(guī)定不管是誰,在用打印機(jī)之前都要向領(lǐng)導(dǎo)申請(qǐng)?jiān)S可證(lock),用完后再向領(lǐng)導(dǎo)歸還許可證(unlock),許可證總共只有一個(gè),沒有許可證的人就等著在用打印機(jī)的同事用完后才能申請(qǐng)?jiān)S可證(阻塞,線程1lock互斥量后其他線程就無法lock,只能等線程1unlock后,其他線程才能lock),那么,這個(gè)許可證就是互斥量?;コ饬勘WC了使用打印機(jī)這一過程不被打斷。
代碼示例:
mutex mtx; int gNum = 0; void Test1() { mtx.lock(); for(int n = 0; n < 5; ++n) gNum++; mtx.unlock(); } void Test2() { std::cout << "gNum = " << gNum << std::endl; } int main() { thread t1(Test1); t1.join(); thread t2(Test2); t2.join(); return 0; }
join()表示主線程等待子線程結(jié)束再繼續(xù)執(zhí)行,如果我們的期望是打印循環(huán)自增之后的gNum的值,那t1.join()就放在t2創(chuàng)建之前調(diào)用。因?yàn)閠2的創(chuàng)建就標(biāo)志著t2線程創(chuàng)建好然后開始執(zhí)行了。
通常mutex不單獨(dú)使用,因?yàn)閘ock和unlock必須配套使用,如果忘記unlock很可能造成死鎖,即使unlock寫了,但是如果在執(zhí)行之前程序捕獲到異常,也還是一樣會(huì)死鎖。如何解決使用mutex造成的死鎖問題呢?下面介紹unique_gard和lock_guard的時(shí)候詳細(xì)說明。
timed_mutex
提供互斥設(shè)施,實(shí)現(xiàn)有時(shí)限鎖定
recursive_mutex
提供能被同一線程遞歸鎖定的互斥設(shè)施
recursive_timed_mutex
提供能被同一線程遞歸鎖定的互斥設(shè)施,并實(shí)現(xiàn)有時(shí)限鎖定
通用互斥管理
lock_guard
void Test1() { std::lock_guard<std::mutex> lg(mtx); for(int n = 0; n < 5; ++n) { gNum++; std::cout << "gNum = " << gNum << std::endl; } } int main() { thread t1(Test1); thread t2(Test1); t1.join(); t2.join(); return 0; }
lock_guard相當(dāng)于利用RAII機(jī)制(“資源獲取就是初始化”)把mutex封裝了一下,在構(gòu)造中l(wèi)ock,在析構(gòu)中unlock。避免了中間過程出現(xiàn)異常導(dǎo)致的mutex不能夠正常unlock.
- scoped_lock(c++17)
- unique_lock
- defer_lock_t
- try_to_lock_t
- adopt_lock_t
- defer_lock
- try_to_lock
- adopt_lock
通用鎖算法
- try_lock
- lock
單次調(diào)用
- once_flag
- call_once
條件變量
- condition_variable
- condition_variable_any
- notify_all_at_thread_exit
- cv_status
Future
- promise
- packaged_task
- future
- shared_future
- async
- launch
- future_status
- Future錯(cuò)誤
- future_error
- future_category
- future_errc
到此這篇關(guān)于c++11新特性多線程操作實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)c++11 多線程操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言數(shù)據(jù)結(jié)構(gòu)實(shí)例講解單鏈表的實(shí)現(xiàn)
單鏈表是后面要學(xué)的雙鏈表以及循環(huán)鏈表的基礎(chǔ),要想繼續(xù)深入了解數(shù)據(jù)結(jié)構(gòu)以及C++,我們就要奠定好這塊基石!接下來就和我一起學(xué)習(xí)吧2022-03-03C++ COM編程之QueryInterface函數(shù)(一)
這篇文章主要介紹了C++ COM編程之QueryInterface函數(shù)(一),QueryInterface是組件本身提供對(duì)自己查詢的一個(gè)接口,需要的朋友可以參考下2014-10-10C++中4種強(qiáng)制類型轉(zhuǎn)換的區(qū)別詳析
這篇文章主要給大家介紹了關(guān)于C++中4種強(qiáng)制類型轉(zhuǎn)換區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03C語言中qsort函數(shù)用法及用冒泡排序?qū)崿F(xiàn)
qsort函數(shù)是由C語言提供的標(biāo)準(zhǔn)庫函數(shù), 它的實(shí)現(xiàn)思想是快速排序。這篇文章主要介紹了C語言中qsort函數(shù)用法及用冒泡排序?qū)崿F(xiàn)qsort函數(shù)功能,需要的可以參考一下2022-10-10