日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > 單片機(jī) > 程序喵大人
[導(dǎo)讀]在多線程開發(fā)過程中很多人應(yīng)該都會(huì)遇到死鎖問題,死鎖問題也是面試過程中經(jīng)常被問到的問題,這里介紹在c++中如何使用gdb+python腳本調(diào)試死鎖問題,以及如何在程序運(yùn)行過程中檢測(cè)死鎖。 首先介紹什么是死鎖,看下維基百科中的定義: 死鎖(英語(yǔ):Deadlock),

多線程開發(fā)過程中很多人應(yīng)該都會(huì)遇到死鎖問題,死鎖問題也是面試過程中經(jīng)常被問到的問題,這里介紹在c++中如何使用gdb+python腳本調(diào)試死鎖問題,以及如何在程序運(yùn)行過程中檢測(cè)死鎖。

首先介紹什么是死鎖,看下維基百科中的定義:

死鎖(英語(yǔ):Deadlock),又譯為死結(jié),計(jì)算機(jī)科學(xué)名詞。當(dāng)兩個(gè)以上的運(yùn)算單元,雙方都在等待對(duì)方停止運(yùn)行,以獲取系統(tǒng)資源,但是沒有一方提前退出時(shí),就稱為死鎖。在多任務(wù)操作系統(tǒng)中,操作系統(tǒng)為了協(xié)調(diào)不同行程,能否獲取系統(tǒng)資源時(shí),為了讓系統(tǒng)運(yùn)作,必須要解決這個(gè)問題。

維基百科中介紹的是進(jìn)程死鎖,多線程中也會(huì)產(chǎn)生死鎖,一樣的道理,這里不作過多介紹。

死鎖的四個(gè)條件

  • 禁止搶占(no preemption):系統(tǒng)資源不能被強(qiáng)制從一個(gè)進(jìn)程(線程)中退出,已經(jīng)獲得的資源在未使用完之前不能被搶占。

  • 等待和保持(hold and wait):一個(gè)進(jìn)程(線程)因請(qǐng)求資源阻塞時(shí),對(duì)已獲得的資源保持不放。

  • 互斥(mutual exclusion):資源只能同時(shí)分配給一個(gè)進(jìn)程(線程),無(wú)法多個(gè)進(jìn)程(線程)共享。

  • 循環(huán)等待(circular waiting):一系列進(jìn)程(線程)互相持有其他進(jìn)程(線程)所需要的資源。

只有同時(shí)滿足以上四個(gè)條件,才會(huì)產(chǎn)生死鎖,想要消除死鎖只需要破壞其中任意一個(gè)條件即可。

如何調(diào)試多線程死鎖問題

多線程出現(xiàn)死鎖的大部分原因都是因?yàn)槎鄠€(gè)線程中加鎖的順序不一致導(dǎo)致的,看如下這段會(huì)出現(xiàn)死鎖的代碼:

using std::cout;
std::mutex mutex1;std::mutex mutex2;std::mutex mutex3;
void FuncA() { std::lock_guard<std::mutex> guard1(mutex1); std::this_thread::sleep_for(std::chrono::seconds(1)); std::lock_guard<std::mutex> guard2(mutex2); std::this_thread::sleep_for(std::chrono::seconds(1));}
void FuncB() { std::lock_guard<std::mutex> guard2(mutex2); std::this_thread::sleep_for(std::chrono::seconds(1)); std::lock_guard<std::mutex> guard3(mutex3); std::this_thread::sleep_for(std::chrono::seconds(1));}
void FuncC() { std::lock_guard<std::mutex> guard3(mutex3); std::this_thread::sleep_for(std::chrono::seconds(1)); std::lock_guard<std::mutex> guard1(mutex1); std::this_thread::sleep_for(std::chrono::seconds(1));}
int main() { std::thread A(FuncA); std::thread B(FuncB); std::thread C(FuncC);
std::this_thread::sleep_for(std::chrono::seconds(5));
if (A.joinable()) { A.join(); } if (B.joinable()) { B.join(); } if (C.joinable()) { C.join(); } cout << "hello\n"; return 0;}

如圖:

  • 線程A已經(jīng)持有mutex1,想要申請(qǐng)mutex2,拿到mutex2后才可以釋放mutex1和mutex2,而此時(shí)mutex2被線程B占用。

  • 線程B已經(jīng)持有mutex2,想要申請(qǐng)mutex3,拿到mutex3后才可以釋放mutex2和mutex3,而此時(shí)mutex3被線程C占用。

  • 線程C已經(jīng)持有mutex3,想要申請(qǐng)mutex1,拿到mutex1后才可以釋放mutex3和mutex1,而此時(shí)mutex1被線程A占用。

三個(gè)線程誰(shuí)也不讓著誰(shuí),導(dǎo)致了死鎖。

傳統(tǒng)gdb調(diào)試多線程死鎖方法

(1)attach id關(guān)聯(lián)到發(fā)生死鎖的進(jìn)程id

(gdb) attach 109Attaching to process 109[New LWP 110][New LWP 111][New LWP 112][Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".0x00007fa33f9e8d2d in __GI___pthread_timedjoin_ex (threadid=140339109693184, thread_return=0x0, abstime=0x0,block=<optimized out>) at pthread_join_common.c:8989 pthread_join_common.c: No such file or directory.

(2)info threads查看當(dāng)前進(jìn)程中所有線程的信息,也可以查看到部分堆棧信息

(gdb) info threadsId Target Id Frame* 1 Thread 0x7fa33ff10740 (LWP 109) "out" 0x00007fa33f9e8d2d in __GI___pthread_timedjoin_ex (threadid=140339109693184, thread_return=0x0, abstime=0x0, block=<optimized out>) at pthread_join_common.c:892 Thread 0x7fa33ec80700 (LWP 110) "out" __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:1353 Thread 0x7fa33e470700 (LWP 111) "out" __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:1354 Thread 0x7fa33dc60700 (LWP 112) "out" __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135

這里可以看到2、3、4線程都在lock_wait狀態(tài),基本上可以看出或許是否問題,但是不一定,這里需要多次info threads看看這些線程有沒有什么變化,多次如果都沒有變化那基本上就是發(fā)生了死鎖。

(3)thread id進(jìn)入具體線程

(gdb) thread 2[Switching to thread 2 (Thread 0x7fa33ec80700 (LWP 110))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135135 ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S: No such file or directory.

(4)bt查看當(dāng)前線程堆棧信息

(gdb) bt#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135#1 0x00007fa33f9ea023 in __GI___pthread_mutex_lock (mutex=0x7fa340204180 <mutex2>) at ../nptl/pthread_mutex_lock.c:78#2 0x00007fa340000fff in __gthread_mutex_lock(pthread_mutex_t*) ()#3 0x00007fa3400015b2 in std::mutex::lock() ()#4 0x00007fa3400016d8 in std::lock_guard<std::mutex>::lock_guard(std::mutex&) ()#5 0x00007fa34000109b in FuncA() ()#6 0x00007fa340001c07 in void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) ()

調(diào)試到這里基本已經(jīng)差不多了,針對(duì)pthread_mutex_t卻可以打印出被哪個(gè)線程持有,之后再重復(fù)步驟3和4,就可以確定哪幾個(gè)線程以及哪幾把鎖發(fā)生的死鎖,而針對(duì)于std::mutex,gdb沒法打印具體的mutex的信息,不能看出來mutex是被哪個(gè)線程持有,只能依次進(jìn)入線程查看堆棧信息。

然而針對(duì)于c++11的std::mutex有沒有什么好辦法定位死鎖呢?

有。

可以算作第五步,繼續(xù):

(5)source加載deadlock.py腳本

(gdb) source -v deadlock.pyType "deadlock" to detect deadlocks.

(6)輸入deadlock檢測(cè)死鎖

(gdb) deadlock[Switching to thread 3 (Thread 0x7f5585670700 (LWP 123))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135135 in ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S[Switching to thread 4 (Thread 0x7f5584e60700 (LWP 124))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135135 in ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S[Switching to thread 2 (Thread 0x7f5585e80700 (LWP 122))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135135 in ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S#1 0x00007f5586bea023 in __GI___pthread_mutex_lock (mutex=0x7f5587404180 <mutex2>) at ../nptl/pthread_mutex_lock.c:78[Switching to thread 3 (Thread 0x7f5585670700 (LWP 123))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135#1 0x00007f5586bea023 in __GI___pthread_mutex_lock (mutex=0x7f55874041c0 <mutex3>) at ../nptl/pthread_mutex_lock.c:78[Switching to thread 4 (Thread 0x7f5584e60700 (LWP 124))]#0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135#1 0x00007f5586bea023 in __GI___pthread_mutex_lock (mutex=0x7f5587404140 <mutex1>) at ../nptl/pthread_mutex_lock.c:78Found deadlock!Thread 2 (LWP 122) is waiting on pthread_mutex_t (0x00007f5587404180) held by Thread 3 (LWP 123)Thread 3 (LWP 123) is waiting on pthread_mutex_t (0x00007f55874041c0) held by Thread 4 (LWP 124)Thread 4 (LWP 124) is waiting on pthread_mutex_t (0x00007f5587404140) held by Thread 2 (LWP 122)

直接看結(jié)果,腳本檢測(cè)出了死鎖,并指明了具體的哪幾個(gè)線程造成的死鎖,根據(jù)輸出信息可以明顯看出來線程鎖形成的環(huán)造成了死鎖,找到了具體是哪幾個(gè)線程構(gòu)成的死鎖環(huán),就可以查看相應(yīng)線程的堆棧信息查看到哪幾把鎖正在等待。

死鎖檢測(cè)腳本的原理

還是拿上面圖舉例:

  • 線程A已經(jīng)持有mutex1,想要申請(qǐng)mutex2,拿到mutex2后才可以釋放mutex1和mutex2,而此時(shí)mutex2被線程B占用。

  • 線程B已經(jīng)持有mutex2,想要申請(qǐng)mutex3,拿到mutex3后才可以釋放mutex2和mutex3,而此時(shí)mutex3被線程C占用。

  • 線程C已經(jīng)持有mutex3,想要申請(qǐng)mutex1,拿到mutex1后才可以釋放mutex3和mutex1,而此時(shí)mutex1被線程A占用。

如圖,三個(gè)線程形成了一個(gè)環(huán),死鎖檢測(cè)就是檢查線程之間是否有環(huán)的存在。單獨(dú)檢查死鎖的環(huán)比較容易,這里延申下還涉及到簡(jiǎn)單環(huán)的概念,因?yàn)檎z測(cè)出來的環(huán)可能是個(gè)大環(huán),不是權(quán)值頂點(diǎn)數(shù)最少的環(huán),如果檢測(cè)的環(huán)的頂點(diǎn)數(shù)較多,加大定位的代價(jià),腳本就是檢測(cè)的簡(jiǎn)單環(huán),這里涉及到強(qiáng)連通分量算法簡(jiǎn)單環(huán)算法,比較繁瑣就不過多介紹了,腳本來源于facebook的folly庫(kù)(這里推薦看下google的abseil和facebook的folly,都是好東西),代碼較長(zhǎng)在文中不好列出,如果有需要的話可以自行下載或者關(guān)注加我好友發(fā)給你。

如何在代碼中檢測(cè)死鎖

和上面介紹的原理相同,在線程加鎖過程中始終維護(hù)一張圖,記錄線程之間的關(guān)系

A->B, B->C, C->A

然后在圖中檢出簡(jiǎn)單環(huán),找到哪幾個(gè)線程處在死鎖狀態(tài),做好狀態(tài)記錄,就可以完備確認(rèn)是具體是哪幾個(gè)線程哪幾把鎖發(fā)生的死鎖,代碼同樣比較長(zhǎng),可以關(guān)注加我好友哦~


REVIEW



往期回顧

一文讓你搞懂設(shè)計(jì)模式
RAII妙用之計(jì)算函數(shù)耗時(shí)
深入淺出虛擬內(nèi)存
C++線程池的實(shí)現(xiàn)之格式修訂版

關(guān)于GDB你需要知道的技巧

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問題卻十分常見,不僅增加了維護(hù)成本,還影響了用戶體驗(yàn)。要解決這一問題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉