成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從騰訊面試題入手,帶你吃透C++死鎖

開(kāi)發(fā) 前端
在多線程編程中,死鎖就像是一場(chǎng)噩夢(mèng),一旦出現(xiàn),程序就如同陷入了無(wú)盡的黑暗,停滯不前。死鎖是指多個(gè)進(jìn)程或線程在執(zhí)行過(guò)程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象 ,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。

想象一下,你駕車行駛在城市的主干道上,本是一路通暢,突然前方車輛停滯不前,起初你以為只是小狀況,等一等就好,結(jié)果等了許久,車輛依舊紋絲未動(dòng)。你下車查看,發(fā)現(xiàn)是兩條道路的車輛在交叉路口互不相讓,都想先行通過(guò),導(dǎo)致誰(shuí)也無(wú)法挪動(dòng),道路陷入了僵局。

這便是現(xiàn)實(shí)生活中的交通堵塞,而在 C++ 編程的多線程世界里,也存在著類似的困境,那就是死鎖。它就像程序中的 “交通堵塞”,讓線程之間相互等待資源,動(dòng)彈不得,導(dǎo)致整個(gè)程序陷入停滯,無(wú)法正常運(yùn)行。今天,就讓我們一起深入探索死鎖的奧秘,了解它究竟是什么,以及在 C++ 中如何巧妙地避免它。

一、什么是死鎖

在多線程編程中,死鎖就像是一場(chǎng)噩夢(mèng),一旦出現(xiàn),程序就如同陷入了無(wú)盡的黑暗,停滯不前。死鎖是指多個(gè)進(jìn)程或線程在執(zhí)行過(guò)程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象 ,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。每個(gè)線程都持有對(duì)方需要的資源,卻又不愿意釋放自己已持有的資源,從而導(dǎo)致所有線程都被阻塞,無(wú)法繼續(xù)執(zhí)行任務(wù)。這種僵持不下的局面,就如同現(xiàn)實(shí)生活中,幾個(gè)人在狹窄的通道中相遇,每個(gè)人都希望對(duì)方先給自己讓路,結(jié)果誰(shuí)也無(wú)法通過(guò)。

1.1死鎖存在的條件

所謂死鎖,是指多個(gè)進(jìn)程在運(yùn)行過(guò)程中因爭(zhēng)奪資源而造成的一種僵局,當(dāng)進(jìn)程處于這種僵持狀態(tài)時(shí),若無(wú)外力作用,它們都將無(wú)法再向前推進(jìn)。

我們舉個(gè)例子來(lái)描述,如果此時(shí)有一個(gè)線程A,按照先鎖a再獲得鎖b的的順序獲得鎖,而在此同時(shí)又有另外一個(gè)線程B,按照先鎖b再鎖a的順序獲得鎖。

圖片圖片

產(chǎn)生死鎖的原因

(1)競(jìng)爭(zhēng)資源——系統(tǒng)中的資源可以分為兩類:

  • 可剝奪資源,是指某進(jìn)程在獲得這類資源后,該資源可以再被其他進(jìn)程或系統(tǒng)剝奪,CPU和主存均屬于可剝奪性資源;
  • 另一類資源是不可剝奪資源,當(dāng)系統(tǒng)把這類資源分配給某進(jìn)程后,再不能強(qiáng)行收回,只能在進(jìn)程用完后自行釋放,如磁帶機(jī)、打印機(jī)等。

產(chǎn)生死鎖中的競(jìng)爭(zhēng)資源之一指的是競(jìng)爭(zhēng)不可剝奪資源(例如:系統(tǒng)中只有一臺(tái)打印機(jī),可供進(jìn)程P1使用,假定P1已占用了打印機(jī),若P2繼續(xù)要求打印機(jī)打印將阻塞)

產(chǎn)生死鎖中的競(jìng)爭(zhēng)資源另外一種資源指的是競(jìng)爭(zhēng)臨時(shí)資源(臨時(shí)資源包括硬件中斷、信號(hào)、消息、緩沖區(qū)內(nèi)的消息等),通常消息通信順序進(jìn)行不當(dāng),則會(huì)產(chǎn)生死鎖

(2) 進(jìn)程間推進(jìn)順序非法

若P1保持了資源R1,P2保持了資源R2,系統(tǒng)處于不安全狀態(tài),因?yàn)檫@兩個(gè)進(jìn)程再向前推進(jìn),便可能發(fā)生死鎖

例如,當(dāng)P1運(yùn)行到P1:Request(R2)時(shí),將因R2已被P2占用而阻塞;當(dāng)P2運(yùn)行到P2:Request(R1)時(shí),也將因R1已被P1占用而阻塞,于是發(fā)生進(jìn)程死鎖

1.2死鎖產(chǎn)生的必要條件

死鎖的產(chǎn)生并非偶然,它需要同時(shí)滿足以下四個(gè)必要條件:

  1. 互斥條件:資源在同一時(shí)間只能被一個(gè)線程或進(jìn)程占用。就像一把鑰匙只能打開(kāi)一把鎖,一個(gè)線程拿到了這把 “鑰匙”(資源),其他線程就無(wú)法同時(shí)使用,必須等待該線程釋放資源后才有機(jī)會(huì)獲取。比如打印機(jī),在某一時(shí)刻只能被一個(gè)進(jìn)程使用,其他進(jìn)程若想使用打印機(jī),就只能排隊(duì)等待。
  2. 請(qǐng)求和保持條件:進(jìn)程已經(jīng)持有了一些資源,但又請(qǐng)求其他被占用的資源,并且在等待新資源的過(guò)程中,不會(huì)釋放已持有的資源。打個(gè)比方,你手里已經(jīng)拿著一個(gè)蘋果(已持資源),還想再拿一個(gè)橙子(請(qǐng)求其他資源),但橙子被別人拿著,你既不放下手里的蘋果,又一直等著別人把橙子給你,這就滿足了請(qǐng)求和保持條件。在多線程場(chǎng)景中,一個(gè)線程已經(jīng)獲取了鎖 A,又試圖獲取鎖 B,而鎖 B 被另一個(gè)線程持有,該線程就會(huì)進(jìn)入等待狀態(tài),同時(shí)仍然持有鎖 A。
  3. 不可搶占條件:資源一旦被某個(gè)進(jìn)程或線程獲得,就只能由持有者主動(dòng)釋放,不能被其他進(jìn)程或線程強(qiáng)行剝奪。這就好比你借了朋友的一本書,朋友不能在你沒(méi)看完的情況下強(qiáng)行把書拿走,只能等你看完主動(dòng)歸還。在程序中,一個(gè)線程獲取的資源,其他線程不能直接搶走,只能等該線程自行釋放資源。
  4. 循環(huán)等待條件:線程或進(jìn)程之間形成一種環(huán)形的等待鏈,鏈中的每個(gè)進(jìn)程都在等待下一個(gè)進(jìn)程所占用的資源。假設(shè)有三個(gè)線程 T1、T2、T3,T1 等待 T2 持有的資源,T2 等待 T3 持有的資源,T3 又等待 T1 持有的資源,這樣就形成了一個(gè)循環(huán)等待的環(huán),導(dǎo)致死鎖的發(fā)生。

這四個(gè)條件就像四條堅(jiān)固的繩索,只有當(dāng)它們同時(shí)束縛住程序時(shí),死鎖才會(huì)降臨。只要我們能破壞其中任何一個(gè)條件,就有望避免死鎖的發(fā)生。

1.3死鎖的危害

死鎖一旦發(fā)生,就會(huì)像一顆隱藏在程序中的定時(shí)炸彈,對(duì)系統(tǒng)造成嚴(yán)重的危害:

  1. 資源浪費(fèi):死鎖會(huì)使系統(tǒng)中的資源被無(wú)效地占用,無(wú)法釋放。因?yàn)樘幱谒梨i狀態(tài)的進(jìn)程或線程不釋放已占有的資源,這些資源就一直無(wú)法被其他進(jìn)程利用,導(dǎo)致資源利用率大幅降低。就好比停車場(chǎng)里的兩輛車,分別占據(jù)了對(duì)方出去的通道,誰(shuí)也動(dòng)不了,使得整個(gè)停車場(chǎng)的資源無(wú)法正常流轉(zhuǎn)。
  2. 程序停滯:死鎖會(huì)導(dǎo)致相關(guān)進(jìn)程或線程被永久阻塞,無(wú)法繼續(xù)執(zhí)行。這意味著程序無(wú)法按照預(yù)期完成任務(wù),用戶可能會(huì)看到程序無(wú)響應(yīng)的情況,極大地影響了用戶體驗(yàn)。例如,一個(gè)圖形界面應(yīng)用程序發(fā)生死鎖,界面會(huì)凍結(jié),用戶的任何操作都得不到回應(yīng)。
  3. 系統(tǒng)崩潰:在一些極端情況下,死鎖可能導(dǎo)致系統(tǒng)崩潰。如果涉及的進(jìn)程占有過(guò)多的資源,死鎖的影響范圍會(huì)逐漸擴(kuò)大,就像病毒一樣蔓延,最終可能導(dǎo)致整個(gè)系統(tǒng)無(wú)法正常運(yùn)行,不得不重啟。比如在一個(gè)大型服務(wù)器系統(tǒng)中,關(guān)鍵進(jìn)程發(fā)生死鎖,可能會(huì)導(dǎo)致整個(gè)服務(wù)器癱瘓,影響大量用戶的使用。

二、C++ 中死鎖的常見(jiàn)場(chǎng)景

2.1多線程多鎖,資源申請(qǐng)順序不一致

在 C++ 多線程編程中,當(dāng)多個(gè)線程需要獲取多個(gè)鎖來(lái)訪問(wèn)共享資源時(shí),如果不同線程獲取鎖的順序不一致,就極有可能導(dǎo)致死鎖。假設(shè)有兩個(gè)線程 A 和 B,以及兩個(gè)互斥鎖 mutex1 和 mutex2 。線程 A 先獲取 mutex1,然后嘗試獲取 mutex2;而線程 B 先獲取 mutex2,再嘗試獲取 mutex1。如果線程 A 獲取了 mutex1,線程 B 獲取了 mutex2,此時(shí)線程 A 等待線程 B 釋放 mutex2,而線程 B 又等待線程 A 釋放 mutex1,這樣就形成了死鎖,兩個(gè)線程都無(wú)法繼續(xù)執(zhí)行。

下面通過(guò)一段具體的代碼來(lái)直觀感受一下這種情況:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void threadA() {
    mutex1.lock();
    std::cout << "線程A獲取了mutex1" << std::endl;

    std::this_thread::sleep_for(std::chrono::milliseconds(100));// 模擬線程A執(zhí)行一些操作

    mutex2.lock();
    std::cout << "線程A獲取了mutex2" << std::endl;

    // 執(zhí)行完操作后,釋放鎖
    mutex2.unlock();
    mutex1.unlock();
}

void threadB() {
    mutex2.lock();
    std::cout << "線程B獲取了mutex2" << std::endl;

    std::this_thread::sleep_for(std::chrono::milliseconds(100));// 模擬線程B執(zhí)行一些操作

    mutex1.lock();
    std::cout << "線程B獲取了mutex1" << std::endl;

    // 執(zhí)行完操作后,釋放鎖
    mutex1.unlock();
    mutex2.unlock();
}

int main() {
    std::thread t1(threadA);
    std::thread t2(threadB);

    t1.join();
    t2.join();

    return 0;
}

在這段代碼中,線程 A 和線程 B 分別嘗試以不同的順序獲取 mutex1 和 mutex2 。當(dāng)程序運(yùn)行時(shí),很可能會(huì)出現(xiàn)線程 A 獲取了 mutex1,線程 B 獲取了 mutex2 的情況,然后兩個(gè)線程互相等待對(duì)方釋放鎖,從而導(dǎo)致死鎖。

2.2線程對(duì)同一個(gè)鎖反復(fù)加鎖

在 C++11 中,std::mutex 是一種非遞歸鎖。這意味著,如果一個(gè)線程已經(jīng)對(duì) std::mutex 加鎖,在沒(méi)有解鎖的情況下再次嘗試加鎖,就會(huì)導(dǎo)致死鎖。因?yàn)榉沁f歸鎖不允許同一個(gè)線程多次獲取,它認(rèn)為同一線程重復(fù)加鎖是一種錯(cuò)誤操作,會(huì)一直等待鎖的釋放,但這個(gè)鎖又被自己持有,所以就陷入了死循環(huán)等待,造成死鎖。

來(lái)看下面這個(gè)示例代碼:

#include <iostream>
#include <mutex>
#include <thread>

std::mutex myMutex;

void recursiveFunction(int count) {
    if (count <= 0) return;

    myMutex.lock();
    std::cout << "遞歸函數(shù),計(jì)數(shù): " << count << std::endl;

    // 遞歸調(diào)用自身,再次嘗試加鎖
    recursiveFunction(count - 1);

    myMutex.unlock();
}

int main() {
    std::thread t(recursiveFunction, 5);
    t.join();

    return 0;
}

在這個(gè)例子中,recursiveFunction函數(shù)是一個(gè)遞歸函數(shù),它每次調(diào)用時(shí)都會(huì)嘗試對(duì)myMutex加鎖。由于myMutex是一個(gè)非遞歸鎖,當(dāng)?shù)谝淮渭渔i后,在遞歸調(diào)用中再次嘗試加鎖時(shí),就會(huì)發(fā)生死鎖,程序會(huì)卡在那里無(wú)法繼續(xù)執(zhí)行。

2.3優(yōu)先級(jí)翻轉(zhuǎn)

在非實(shí)時(shí)操作系統(tǒng)中,當(dāng)進(jìn)行了線程優(yōu)先級(jí)劃分時(shí),就可能出現(xiàn)優(yōu)先級(jí)翻轉(zhuǎn)的情況,進(jìn)而導(dǎo)致死鎖。具體來(lái)說(shuō),低優(yōu)先級(jí)的線程可能會(huì)持有高優(yōu)先級(jí)線程所需要的鎖,由于非實(shí)時(shí)操作系統(tǒng)不能保證所有線程在一定時(shí)間段內(nèi)一定會(huì)被調(diào)用,低優(yōu)先級(jí)線程可能長(zhǎng)時(shí)間得不到調(diào)度,無(wú)法執(zhí)行釋放鎖的代碼,高優(yōu)先級(jí)線程就會(huì)因?yàn)榈却蛢?yōu)先級(jí)線程釋放鎖而被阻塞。如果此時(shí)高優(yōu)先級(jí)線程還持有了低優(yōu)先級(jí)線程需要的鎖,那么就一定會(huì)導(dǎo)致死鎖。

假設(shè)有三個(gè)線程:高優(yōu)先級(jí)線程 H、中優(yōu)先級(jí)線程 M 和低優(yōu)先級(jí)線程 L,并且線程 H 和線程 L 共享一個(gè)資源,通過(guò)一個(gè)互斥鎖來(lái)保護(hù)。當(dāng)線程 L 獲取了鎖并開(kāi)始執(zhí)行,此時(shí)線程 H 被喚醒,由于其優(yōu)先級(jí)高,搶占了線程 L 的執(zhí)行權(quán)。但線程 H 需要線程 L 持有的鎖才能繼續(xù)執(zhí)行,所以線程 H 進(jìn)入等待狀態(tài)。之后,線程 M 被喚醒并開(kāi)始執(zhí)行,由于線程 L 的優(yōu)先級(jí)低于線程 M,線程 L 無(wú)法獲得 CPU 執(zhí)行權(quán),也就無(wú)法釋放鎖,線程 H 就會(huì)一直等待。如果線程 H 在等待過(guò)程中,又獲取了線程 L 需要的另一個(gè)鎖,而線程 L 在之后嘗試獲取這個(gè)鎖時(shí),就會(huì)因?yàn)榫€程 H 持有該鎖而等待,這樣就形成了死鎖,線程 H 和線程 L 都無(wú)法繼續(xù)執(zhí)行 。

三、如何在 C++ 中避免死鎖

在了解了 C++ 中死鎖的常見(jiàn)場(chǎng)景后,我們就可以針對(duì)性地采取措施來(lái)避免死鎖的發(fā)生。下面為大家介紹幾種在 C++ 中有效避免死鎖的方法。

3.1使用相同順序獲取鎖

為了避免因資源申請(qǐng)順序不一致導(dǎo)致的死鎖,我們可以讓所有線程按照相同的順序獲取鎖。比如,在前面提到的多線程多鎖的場(chǎng)景中,如果線程 A 和線程 B 都先獲取 mutex1,再獲取 mutex2 ,就不會(huì)出現(xiàn)死鎖的情況。這種方法就像是制定了一個(gè)交通規(guī)則,所有車輛都按照統(tǒng)一的順序行駛,就不會(huì)出現(xiàn)堵塞。

3.2使用 std::lock 同時(shí)獲取多把鎖

C++ 標(biāo)準(zhǔn)庫(kù)提供了 std::lock 函數(shù),它可以同時(shí)鎖住多個(gè)互斥量,并且內(nèi)部使用了無(wú)死鎖的算法(如 Twins 算法)來(lái)鎖定這些互斥量,從而確保不會(huì)發(fā)生死鎖 。使用 std::lock 時(shí),需要配合 std::lock_guard 或者 std::unique_lock 使用,使其能夠自動(dòng)解鎖,這樣可以避免手動(dòng)解鎖時(shí)可能出現(xiàn)的錯(cuò)誤。例如:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void threadFunction() {
    std::lock(mutex1, mutex2);
    std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);

    // 訪問(wèn)共享資源的代碼
    std::cout << "線程成功獲取了mutex1和mutex2" << std::endl;
}

int main() {
    std::thread t(threadFunction);
    t.join();

    return 0;
}

在這個(gè)例子中,std::lock 同時(shí)鎖住了 mutex1 和 mutex2 ,然后通過(guò) std::lock_guard 來(lái)管理鎖的生命周期,確保在離開(kāi)作用域時(shí)自動(dòng)解鎖,避免了死鎖的發(fā)生。

3.3使用 std::scoped_lock 同時(shí)獲取多把鎖

C++17 引入了 std::scoped_lock,它是一個(gè)可變函數(shù)模板,可以接收多個(gè)互斥量作為參數(shù),在構(gòu)造時(shí)自動(dòng)調(diào)用 std::lock 來(lái)同時(shí)獲取這些互斥量,并在析構(gòu)時(shí)自動(dòng)解鎖。std::scoped_lock 的出現(xiàn),讓同時(shí)獲取多個(gè)互斥量變得更加簡(jiǎn)潔和安全,有效避免了死鎖的風(fēng)險(xiǎn)。例如:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void threadFunction() {
    std::scoped_lock lock(mutex1, mutex2);

    // 訪問(wèn)共享資源的代碼
    std::cout << "線程成功獲取了mutex1和mutex2" << std::endl;
}

int main() {
    std::thread t(threadFunction);
    t.join();

    return 0;
}

在這段代碼中,std::scoped_lock 自動(dòng)管理了 mutex1 和 mutex2 的鎖定和解鎖,使得代碼更加簡(jiǎn)潔明了,同時(shí)也保證了不會(huì)發(fā)生死鎖。

3.4避免嵌套鎖,使用遞歸鎖

盡量避免在一個(gè)線程中請(qǐng)求多個(gè)鎖,減少嵌套鎖的使用。如果確實(shí)需要使用嵌套鎖,應(yīng)該使用遞歸鎖,如 std::recursive_mutex 。std::recursive_mutex 允許同一個(gè)線程多次獲取鎖,而不會(huì)導(dǎo)致死鎖。例如:

#include <iostream>
#include <thread>
#include <mutex>

std::recursive_mutex recursiveMutex;

void recursiveFunction(int count) {
    if (count <= 0) return;

    recursiveMutex.lock();
    std::cout << "遞歸函數(shù),計(jì)數(shù): " << count << std::endl;

    // 遞歸調(diào)用自身,再次嘗試加鎖
    recursiveFunction(count - 1);

    recursiveMutex.unlock();
}

int main() {
    std::thread t(recursiveFunction, 5);
    t.join();

    return 0;
}

在這個(gè)例子中,使用 std::recursive_mutex 作為遞歸鎖,使得遞歸函數(shù)可以多次獲取鎖,避免了因重復(fù)加鎖導(dǎo)致的死鎖問(wèn)題。

3.5引入鎖的等級(jí)制度

我們可以對(duì)鎖進(jìn)行等級(jí)劃分,規(guī)定線程在持有低等級(jí)鎖時(shí),不能獲取高等級(jí)的鎖。這樣可以避免線程之間形成循環(huán)等待的情況,從而預(yù)防死鎖的發(fā)生。例如,將鎖分為一級(jí)鎖、二級(jí)鎖和三級(jí)鎖,線程在獲取二級(jí)鎖之前,必須先釋放所有一級(jí)鎖;獲取三級(jí)鎖之前,必須先釋放所有一級(jí)鎖和二級(jí)鎖。通過(guò)這種方式,可以有效地打破死鎖產(chǎn)生的循環(huán)等待條件。

四、C++死鎖案例分析

4.1代碼示例

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void threadFunction1() {
    std::lock_guard<std::mutex> lock1(mutex1);
    std::cout << "Thread 1 acquired mutex1." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock2(mutex2);
    std::cout << "Thread 1 acquired mutex2." << std::endl;
}

void threadFunction2() {
    std::lock_guard<std::mutex> lock2(mutex2);
    std::cout << "Thread 2 acquired mutex2." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock1(mutex1);
    std::cout << "Thread 2 acquired mutex1." << std::endl;
}

int main() {
    std::thread t1(threadFunction1);
    std::thread t2(threadFunction2);

    t1.join();
    t2.join();

    return 0;
}

在這個(gè)案例中,我們有兩個(gè)線程t1和t2,以及兩個(gè)互斥鎖mutex1和mutex2。線程t1首先獲取mutex1,然后嘗試獲取mutex2;而線程t2則先獲取mutex2,再嘗試獲取mutex1。當(dāng)兩個(gè)線程同時(shí)運(yùn)行時(shí),就可能出現(xiàn)這樣的情況:線程t1持有mutex1并等待mutex2,而線程t2持有mutex2并等待mutex1,從而形成死鎖。

4.2死鎖成因剖析

  • 互斥條件:互斥鎖的特性決定了同一時(shí)間只能有一個(gè)線程訪問(wèn)共享資源。在上述案例中,mutex1 和 mutex2 都具有互斥性,這是死鎖產(chǎn)生的基礎(chǔ)條件。
  • 請(qǐng)求和保持條件:線程 t1 在持有 mutex1 的同時(shí)請(qǐng)求 mutex2,線程 t2 在持有 mutex2 的同時(shí)請(qǐng)求 mutex1,滿足請(qǐng)求和保持條件。
  • 不剝奪條件:一旦線程獲取了互斥鎖,除非它主動(dòng)釋放,否則其他線程無(wú)法強(qiáng)行剝奪該鎖。這使得線程在等待其他鎖時(shí)會(huì)一直持有已有的鎖,增加了死鎖的可能性。
  • 循環(huán)等待條件:線程 t1 和 t2 形成了一個(gè)循環(huán)等待鏈,t1 等待 t2 持有的 mutex2,t2 等待 t1 持有的 mutex1,滿足循環(huán)等待條件。

4.3死鎖檢測(cè)方法

  • 日志記錄:在代碼中添加日志記錄,記錄線程獲取和釋放鎖的時(shí)間和順序。通過(guò)分析日志,可以發(fā)現(xiàn)線程之間的鎖競(jìng)爭(zhēng)情況,從而判斷是否存在死鎖的可能性。
  • 調(diào)試工具:使用調(diào)試工具(如 Visual Studio 的調(diào)試器)可以在程序運(yùn)行時(shí)暫停線程,查看線程的狀態(tài)和持有鎖的情況。這有助于定位死鎖發(fā)生的具體位置。
  • 靜態(tài)代碼分析工具:靜態(tài)代碼分析工具可以對(duì)代碼進(jìn)行掃描,檢查是否存在潛在的死鎖問(wèn)題。例如,PVS-Studio 可以檢測(cè)出代碼中鎖的獲取和釋放順序不一致等問(wèn)題。

4.4死鎖解決策略

(1)統(tǒng)一鎖獲取順序

為了避免循環(huán)等待條件的出現(xiàn),可以規(guī)定所有線程都按照相同的順序獲取鎖。在上述案例中,我們可以讓兩個(gè)線程都先獲取 mutex1,再獲取 mutex2。修改后的代碼如下:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void threadFunction1() {
    std::lock_guard<std::mutex> lock1(mutex1);
    std::cout << "Thread 1 acquired mutex1." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock2(mutex2);
    std::cout << "Thread 1 acquired mutex2." << std::endl;
}

void threadFunction2() {
    std::lock_guard<std::mutex> lock1(mutex1);
    std::cout << "Thread 2 acquired mutex1." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock2(mutex2);
    std::cout << "Thread 2 acquired mutex2." << std::endl;
}

int main() {
    std::thread t1(threadFunction1);
    std::thread t2(threadFunction2);

    t1.join();
    t2.join();

    return 0;
}

(2) 使用 std::lock

C++ 標(biāo)準(zhǔn)庫(kù)提供了 std::lock 函數(shù),可以同時(shí)獲取多個(gè)鎖,避免死鎖的發(fā)生。修改后的代碼如下:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void threadFunction1() {
    std::lock(mutex1, mutex2);
    std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);
    std::cout << "Thread 1 acquired both mutexes." << std::endl;
}

void threadFunction2() {
    std::lock(mutex1, mutex2);
    std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);
    std::cout << "Thread 2 acquired both mutexes." << std::endl;
}

int main() {
    std::thread t1(threadFunction1);
    std::thread t2(threadFunction2);

    t1.join();
    t2.join();

    return 0;
}

(3)超時(shí)機(jī)制

在獲取鎖時(shí)設(shè)置超時(shí)時(shí)間,如果在規(guī)定時(shí)間內(nèi)無(wú)法獲取鎖,則放棄并進(jìn)行其他處理。這樣可以避免線程無(wú)限等待,減少死鎖的可能性。


責(zé)任編輯:武曉燕 來(lái)源: 深度Linux
相關(guān)推薦

2025-05-06 08:20:00

互斥鎖C++編程

2021-10-27 11:00:30

C++語(yǔ)言面試

2010-01-28 16:58:32

學(xué)習(xí)C++感想

2025-05-23 08:15:00

C++constexpr字面類型

2025-05-20 10:00:00

C++命名空間別名代碼

2025-03-24 00:11:05

IO模型計(jì)算機(jī)

2011-03-29 14:31:41

CC++

2025-05-27 10:15:00

void*函數(shù)開(kāi)發(fā)

2025-04-30 10:10:00

在 C++C++11Lambda

2025-05-20 08:10:00

函數(shù)函數(shù)類型函數(shù)指針類型

2020-06-04 14:40:40

面試題Vue前端

2025-06-05 08:05:00

vectorC++對(duì)象存儲(chǔ)

2020-11-16 07:22:32

騰訊多線程

2025-06-09 07:55:00

C++引用語(yǔ)言

2011-03-24 13:27:37

SQL

2023-11-13 07:37:36

JS面試題線程

2025-04-27 02:33:00

epoll核心機(jī)制服務(wù)器

2009-08-28 09:29:02

2025-02-11 07:40:27

2012-06-26 11:09:07

Web
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品一区一区 | 国产精品视频播放 | 国产精品毛片 | 无人区国产成人久久三区 | av一区二区三区四区 | 黄色欧美在线 | 亚洲第一天堂 | 日日干夜夜操 | 亚洲一区二区av | 欧美激情a∨在线视频播放 成人免费共享视频 | 欧美一级淫片免费视频黄 | 成人性生交大免费 | 欧美亚洲国产一区二区三区 | 国产色婷婷精品综合在线手机播放 | 日韩有码在线播放 | 久久久久一区二区 | 久久精品国产久精国产 | 中文字幕91 | 久久免费精品视频 | 瑟瑟视频在线看 | 毛片视频网址 | 日韩91| 亚洲影音先锋 | 神马福利| 午夜免费视频 | 一级黄色裸片 | 日韩欧美在线视频一区 | 日本精品一区二区 | 91精品国产综合久久久密闭 | 日韩中文在线视频 | 亚洲精品国产a久久久久久 午夜影院网站 | 一级黄色夫妻生活 | 欧美国产中文 | 国产精品久久久久9999鸭 | 欧美精品一区二区在线观看 | 91在线免费视频 | 91久久北条麻妃一区二区三区 | 免费一区在线 | 久久久久久久久国产 | 国产在线精品一区二区 | 一区二区视频在线 |