網(wǎng)站首頁 編程語言 正文
我們了解互斥量和條件變量之前,我們先來看一下為什么要有互斥量和條件變量這兩個東西,了解為什么有這兩東西之后,理解起來后面的東西就簡單很多了!!!
先來看下面這段簡單的代碼:
int g_num = 0; void print(int id) { for (int i = 0; i < 5; i++) { ++g_num; cout << "id = " << id << "==>" << g_num << endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } } int main() { thread tha(print, 0); thread thb(print, 1); tha.join(); thb.join(); return 0; }
上述代碼功能大致就是在線程tha和thb中運(yùn)行函數(shù)print,每個線程對g_num進(jìn)行加加一次,最后加出來的g_num的值應(yīng)該是10,那么我們現(xiàn)在來看結(jié)果:
我們看到運(yùn)行結(jié)果,為什么打印結(jié)果最后,按理來說兩個線程各加五次,最后結(jié)果應(yīng)該是10呀,怎么會是9呢?
?如上圖所示,是因?yàn)?+這個運(yùn)算符不是原子操作(不會被線程調(diào)度機(jī)制打斷的操作),我們可以將g_num設(shè)置為原子數(shù),改為atomic_int g_num = 0;
atomic_int g_num = 0; //將g_num設(shè)置為原子操作數(shù) //atomicg_num = 0;這個和上面是一樣的 下面這行是模板化之后的 void print(int id) { for (int i = 0; i < 5; i++) { ++g_num; cout << "id = " << id << "==>" << g_num << endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } } int main() { thread tha(print, 0); thread thb(print, 1); tha.join(); thb.join(); return 0; }
將g_num設(shè)置為原子操作數(shù)之后,在++階段就不會被線程調(diào)度機(jī)制給打斷,我們來看運(yùn)行結(jié)果:
運(yùn)行結(jié)果是我所期望的但是中間那塊又出了一點(diǎn)小狀況連著打著兩個4,兩個6,這種情況該怎么辦呢?
下面就該說道我們的互斥鎖了:
互斥鎖:
在上述代碼中我們使用了共享資源----->全局量g_num,兩個線程同時對g_num進(jìn)行++操作,為了保護(hù)共享資源,在線程里也有這么一把鎖——互斥鎖(mutex),互斥鎖是一種簡單的加鎖的方法來控制對共享資源的訪問,互斥鎖只有兩種狀態(tài),即上鎖( lock )和解鎖( unlock )。
來看下面代碼:
int g_num = 0; std::mutex mtx; //創(chuàng)建鎖對象 void print(int id) { for (int i = 0; i < 5; i++) { mtx.lock(); //上鎖 ++g_num; cout << "id = " << id << "==>" << g_num << endl; mtx.unlock(); //解鎖 std::this_thread::sleep_for(std::chrono::microseconds(500)); } } int main() { thread tha(print, 0); thread thb(print, 1); tha.join(); thb.join(); return 0; }
我們來看運(yùn)行結(jié)果:符合我們最初的預(yù)期。
?打開官方文檔,可以看到
創(chuàng)建鎖對象只有這個方法,拷貝構(gòu)造被刪除了?。
std::mutex::try_lock ? ? ? ??
對于互斥鎖的lock和unlock我們都很熟悉了,下面來說一下std::mutex::try_lock
這個成員函數(shù)!
?try_lock字面意思就是說嘗試上鎖,如果上鎖成功,返回true,上鎖失敗則返回false,但是如果上鎖失敗,他還是會接著往下運(yùn)行,不會像lock哪運(yùn)被阻塞在上鎖那塊,所以try_lock必須得在循環(huán)中使用:
int g_num = 0; std::mutex mtx; void print(int id) { for (int i = 0; i < 5; i++) { mtx.try_lock(); //代碼只是將lock換成了try_lock且沒把try_lock扔在循環(huán)中執(zhí)行 ++g_num; cout << "id = " << id << "==>" << g_num << endl; mtx.unlock(); std::this_thread::sleep_for(std::chrono::microseconds(500)); } } int main() { thread tha(print, 0); thread thb(print, 1); tha.join(); thb.join(); return 0; }
我們來看運(yùn)行結(jié)果:
?unlock of? unowned? mutex,這玩意思就是說你在給個沒上鎖的互斥鎖解鎖,所以報(bào)這錯誤,因此try_lock擱在普通語句中,會有很大的問題,現(xiàn)命我們演示一下將這玩意放到循環(huán)中去弄一邊:
int g_num = 0; std::mutex mtx; void print(int id) { for (int i = 0; i < 5; i++) { while (!mtx.try_lock()) //try_lock失敗時為false 前面加了!,所以失敗時為true 然后打印嘗試加鎖 { //然后再次嘗試加鎖,只有加鎖成功了,才能出這個while循環(huán) cout << "try lock" << endl; } ++g_num; cout << "id = " << id << "==>" << g_num << endl; mtx.unlock(); std::this_thread::sleep_for(std::chrono::microseconds(500)); } } int main() { thread tha(print, 0); thread thb(print, 1); tha.join(); thb.join(); return 0; }
我們來看運(yùn)行結(jié)果:
?運(yùn)行結(jié)果符合我們的預(yù)期,但是try_lock這個函數(shù)有個不好處是太損耗資源了,當(dāng)它加鎖失敗時,一直嘗試加鎖一直嘗試加鎖,損耗CPU資源。
條件變量:condition_variable
框住這三個函數(shù)較為重要,下面著重來說下面這三個函數(shù):?這里順便說一下,下面代碼將會是條件變量和互斥鎖的結(jié)合使用,至于為什么要將互斥鎖和條件變量一起使用,原因就是互斥鎖狀態(tài)太單一了,而條件變量允許阻塞,接收信號量等剛好彌補(bǔ)了互斥鎖的缺陷所以這些一起使用!!!
這三個函數(shù)呢,通過一個小實(shí)驗(yàn)來實(shí)現(xiàn),通過多線程分別打印123一直到100:
std::mutex mtx; std::condition_variable cv; int isReady = 0; const int n = 100; void print_A() { std::unique_locklock(mtx); //unique_lock相當(dāng)于線程中的智能制造 自動解鎖,不需要再unlock int i = 0; while (i < n) { while (isReady != 0) { cv.wait(lock);//互斥鎖和信號量一起使用 wait參數(shù)為鎖對象 } cout << "A" ; isReady = 1; ++i; std::this_thread::sleep_for(std::chrono::microseconds(100)); cv.notify_all(); //當(dāng)isReady等于0時print_B 和 print_C 處于阻塞狀態(tài) //該函數(shù)就是喚醒所有等待的函數(shù),然后通過isReady來進(jìn)行判斷要進(jìn)行那個函數(shù)的運(yùn)行 } } void print_B() { std::unique_lock lock(mtx); //unique_lock相當(dāng)于線程中的智能制造 自動解鎖,不需要再unlock int i = 0; while (i < n) { while (isReady != 1) { cv.wait(lock); } cout << "B" ; isReady = 2; ++i; std::this_thread::sleep_for(std::chrono::microseconds(100)); cv.notify_all(); } } void print_C() { std::unique_lock lock(mtx); //unique_lock相當(dāng)于線程中的智能制造 自動解鎖,不需要再unlock int i = 0; while (i < n) { while (isReady != 2) { cv.wait(lock); } cout << "C" ; isReady = 0; ++i; std::this_thread::sleep_for(std::chrono::microseconds(100)); cv.notify_all(); } } int main() { thread tha(print_A); thread thb(print_B); thread thc(print_C); tha.join(); thb.join(); thc.join(); return 0; }
上面代碼解析:
運(yùn)行結(jié)果:
我們可以看到上述代碼最后喚醒其他線程使用的是notify_all()函數(shù),notify_all()函數(shù)作用就是環(huán)球其他阻塞的函數(shù),然后因?yàn)閕sready這個數(shù)的存在,所以就會選擇合適的線程來進(jìn)行執(zhí)行,如果我們使用notify_one()呢,先來說下notify_one()函數(shù)的作用是什么。notify_one()函數(shù)是喚醒其他線程(隨機(jī)喚醒,這道題中不適合,因?yàn)槿绻蛴⊥闍之后喚醒了C線程那么就會一直阻塞在那塊)
我們試一下notify_one()函數(shù),可以發(fā)現(xiàn)這玩意確實(shí)會堵塞在那塊:
總結(jié)
原文鏈接:https://blog.csdn.net/qq_45829112/article/details/123541772
相關(guān)推薦
- 2022-08-15 數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組棧的實(shí)現(xiàn)
- 2022-05-04 Python的五個標(biāo)準(zhǔn)數(shù)據(jù)類型你認(rèn)識幾個_python
- 2023-01-30 Python中String模塊示例詳解_python
- 2023-05-31 Pandas使用分隔符或正則表達(dá)式將字符串拆分為多列_python
- 2022-07-11 Oracle使用dblink同步數(shù)據(jù)
- 2022-08-15 Docker常見用法
- 2022-01-03 antd獲取表單的所有數(shù)據(jù)
- 2022-06-13 docker容器的四種端口映射演示分析_docker
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支