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)建起來挺麻煩的,一般場景下要求線程睡眠的就用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)文章
VS2022新建項(xiàng)目時(shí)沒有ASP.NET Web應(yīng)用程序(.NET Framework)
本文主要介紹了VS2022新建項(xiàng)目時(shí)沒有ASP.NET Web應(yīng)用程序的解決,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-10-10
C++中指向?qū)ο蟮某V羔樑c指向常對(duì)象的指針詳解
如果一個(gè)變量已經(jīng)被聲明成常變量,則只能用指向常變量的指針變量指向它,而不能用一般的(非const型的)指針變量指向它2013-10-10
C# CLR 中學(xué)習(xí) C++關(guān)鍵詞extern使用詳解
這篇文章主要為大家介紹了C# CLR 中學(xué)習(xí) C++ 之extern使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
C語言詳解如何實(shí)現(xiàn)堆及堆的結(jié)構(gòu)與接口
堆是計(jì)算機(jī)科學(xué)中一類特殊的數(shù)據(jù)結(jié)構(gòu)的統(tǒng)稱,通常是一個(gè)可以被看做一棵完全二叉樹的數(shù)組對(duì)象。而堆排序是利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計(jì)的一種排序算法。本文將詳細(xì)介紹堆的結(jié)構(gòu)與接口,需要的可以參考一下2022-04-04
c++ 一個(gè)二進(jìn)制串轉(zhuǎn)化為整數(shù)的解決方法
以下是將一個(gè)二進(jìn)制串轉(zhuǎn)化為整數(shù)的實(shí)例。需要的朋友參考下2013-05-05
詳解C語言用malloc函數(shù)申請(qǐng)二維動(dòng)態(tài)數(shù)組的實(shí)例
這篇文章主要介紹了詳解C語言用malloc函數(shù)申請(qǐng)二維動(dòng)態(tài)數(shù)組的實(shí)例的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-10-10

