欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++11鎖機(jī)制mutex、lock_guard與unique_lock詳細(xì)解析

 更新時(shí)間:2025年05月26日 09:17:00   作者:xiaokang-coding  
這篇文章主要介紹了C++11鎖機(jī)制mutex、lock_guard與unique_lock詳細(xì)解析,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

大家好啊,我是小康。今天咱們聊點(diǎn)"家常"——那些讓C++程序員又愛又恨的多線程同步工具!

如果你曾經(jīng)被多線程搞得頭大,或者聽到"死鎖"就心慌,那這篇文章就是為你準(zhǔn)備的。今天我要用最接地氣的方式,幫你徹底搞懂C++11中的三兄弟:mutex、lock_guardunique_lock

為啥要用這些同步工具?

先別急著學(xué)怎么用,咱們得先知道為啥要用??!

想象一下:你和室友共用一個(gè)衛(wèi)生間。如果你們同時(shí)沖進(jìn)去...嗯,畫面太美不敢想象。所以你們會(huì)怎么做?肯定是先看看有沒有人,沒人才進(jìn)去,然后反鎖門,用完了再開門。

多線程程序也一樣!不同的線程可能會(huì)同時(shí)訪問同一塊"地盤"(共享資源),如果不加控制,就會(huì)出現(xiàn)數(shù)據(jù)錯(cuò)亂、程序崩潰等一系列災(zāi)難。

這時(shí)候,我們的三兄弟就閃亮登場了!

老大:mutex(互斥鎖)

mutex就像那個(gè)衛(wèi)生間的門鎖,它是最基礎(chǔ)的同步工具,核心功能就兩個(gè):鎖上(lock)和開鎖(unlock)。

來看個(gè)最簡單的例子:

#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;  // 這就是我們的"門鎖"
int shared_value = 0;  // 這是我們要保護(hù)的"衛(wèi)生間"
void increment_value() {
    mtx.lock();  // 進(jìn)去之前先鎖門
    std::cout << "線程 " << std::this_thread::get_id() << " 進(jìn)入臨界區(qū)" << std::endl;
    // 想象這是個(gè)很復(fù)雜的操作,需要一些時(shí)間
    shared_value++;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "線程 " << std::this_thread::get_id() << " 即將離開,共享值為: " << shared_value << std::endl;
    mtx.unlock();  // 用完了記得開鎖,讓別人能進(jìn)來
}
int main() {
    std::thread t1(increment_value);
    std::thread t2(increment_value);
    t1.join();
    t2.join();
    return 0;
}

看著挺簡單對吧?但這有個(gè)大坑——如果在lockunlock之間發(fā)生了異常,或者你單純忘記了unlock,那么鎖就永遠(yuǎn)不會(huì)被釋放,其他線程永遠(yuǎn)進(jìn)不了"衛(wèi)生間"!這就是傳說中的"死鎖"。

正因如此,直接使用mutex很容易出錯(cuò),所以C++11給我們提供了更智能的解決方案。

老二:lock_guard(保安大哥)

lock_guard就像一個(gè)靠譜的保安大哥。當(dāng)你進(jìn)"衛(wèi)生間"時(shí),他會(huì)自動(dòng)鎖門;當(dāng)你出來時(shí),無論是正常出來還是因?yàn)橥话l(fā)情況(異常)跑出來,他都會(huì)負(fù)責(zé)解鎖。

看看用lock_guard如何改寫上面的例子:

void safer_increment() {
    std::lock_guard<std::mutex> guard(mtx);  // 保安上崗,自動(dòng)鎖門
    std::cout << "線程 " << std::this_thread::get_id() << " 進(jìn)入臨界區(qū)" << std::endl;
    // 即使這里拋出異常,離開函數(shù)作用域時(shí)lock_guard也會(huì)自動(dòng)解鎖
    shared_value++;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "線程 " << std::this_thread::get_id() << " 即將離開,共享值為: " << shared_value << std::endl;
    // 不需要手動(dòng)解鎖,guard離開作用域時(shí)會(huì)自動(dòng)解鎖
}

是不是簡單多了?這就是RAII(資源獲取即初始化)的魅力——資源的管理跟對象的生命周期綁定在一起。lock_guard一旦創(chuàng)建就會(huì)鎖定互斥量,一旦銷毀(離開作用域)就會(huì)解鎖互斥量。

不過lock_guard有個(gè)局限性:一旦上鎖,在其生命周期內(nèi)你就不能手動(dòng)解鎖了。就像你請了個(gè)特別死板的保安,他堅(jiān)持要等你徹底離開才會(huì)開門,中途想出去透個(gè)氣都不行。

老三:unique_lock(萬能管家)

如果說lock_guard是保安大哥,那unique_lock就是一個(gè)高級(jí)管家,不但能自動(dòng)鎖門解鎖,還能根據(jù)你的指令隨時(shí)鎖門或開門,甚至可以"借"鑰匙給別人。

來看個(gè)例子:

void flexible_operation() {
    std::unique_lock<std::mutex> superlock(mtx);  // 默認(rèn)情況下構(gòu)造時(shí)會(huì)鎖定mutex
    std::cout << "線程 " << std::this_thread::get_id() << " 開始工作" << std::endl;
    shared_value++;
    // 假設(shè)這里不需要鎖了,可以提前解鎖
    superlock.unlock();
    std::cout << "臨時(shí)解鎖,執(zhí)行一些不需要保護(hù)的操作" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    // 需要再次訪問共享資源時(shí),可以重新上鎖
    superlock.lock();
    shared_value++;
    std::cout << "線程 " << std::this_thread::get_id() << " 完成工作,共享值為: " << shared_value << std::endl;
    // 同樣,不需要手動(dòng)解鎖,離開作用域時(shí)會(huì)自動(dòng)解鎖(如果當(dāng)時(shí)處于鎖定狀態(tài))
}

除了手動(dòng)lockunlockunique_lock還有更多高級(jí)功能:

std::unique_lock<std::mutex> master_lock(mtx, std::defer_lock);  // 創(chuàng)建時(shí)不鎖定
if (master_lock.try_lock()) {  // 嘗試鎖定,如果失敗也不會(huì)阻塞
    std::cout << "成功獲取鎖!" << std::endl;
} else {
    std::cout << "獲取鎖失敗,但我可以去做別的事" << std::endl;
}
// 還可以配合條件變量使用
std::condition_variable cv;
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });  // 這里會(huì)自動(dòng)解鎖并等待條件滿足

unique_locklock_guard靈活,但也付出了一點(diǎn)性能代價(jià),它內(nèi)部需要維護(hù)更多狀態(tài)信息。

三兄弟大比拼

說了這么多,來個(gè)簡單對比:

特性mutexlock_guardunique_lock
手動(dòng)鎖定/解鎖???
異常安全?(需手動(dòng)保證)??
條件變量配合???
嘗試鎖定(try_lock)???
性能開銷最小很小稍大
使用難度容易出錯(cuò)簡單安全靈活但復(fù)雜

實(shí)戰(zhàn):模擬ATM取款與系統(tǒng)維護(hù)

最后用一個(gè)貼近生活的例子來鞏固一下。假設(shè)我們有個(gè)ATM系統(tǒng),既要處理用戶取款,又要處理銀行的系統(tǒng)維護(hù):

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
class ATMSystem {
private:
    double cash_available;  // ATM中可用現(xiàn)金
    bool maintenance_mode;  // 是否處于維護(hù)模式
    std::mutex mtx;
    std::condition_variable cv;  // 條件變量,用于等待維護(hù)結(jié)束
public:
    ATMSystem(double initial_cash) : cash_available(initial_cash), maintenance_mode(false) {}
    // 用戶取款操作
    bool withdraw(double amount) {
        // 這里必須用unique_lock,因?yàn)闂l件變量wait需要它
        std::unique_lock<std::mutex> lock(mtx);
        // 如果ATM正在維護(hù)中,等待維護(hù)結(jié)束
        cv.wait(lock, [this] { return !maintenance_mode; });
        // 檢查余額并取款
        if (cash_available >= amount) {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            cash_available -= amount;
            std::cout << "取出: " << amount << ",ATM剩余現(xiàn)金: " << cash_available << std::endl;
            return true;
        }
        std::cout << "ATM現(xiàn)金不足,取款失??!當(dāng)前剩余: " << cash_available << std::endl;
        return false;
    }
    // 開始系統(tǒng)維護(hù)
    void start_maintenance() {
        std::lock_guard<std::mutex> guard(mtx);
        maintenance_mode = true;
        std::cout << "ATM進(jìn)入維護(hù)模式,暫停服務(wù)" << std::endl;
    }
    // 結(jié)束系統(tǒng)維護(hù)
    void end_maintenance() {
        {
            std::lock_guard<std::mutex> guard(mtx);
            maintenance_mode = false;
            std::cout << "ATM維護(hù)完成,恢復(fù)服務(wù)" << std::endl;
        }
        // 通知所有等待的取款線程
        cv.notify_all();
    }
    // 補(bǔ)充現(xiàn)金
    void refill_cash(double amount) {
        std::lock_guard<std::mutex> guard(mtx);
        cash_available += amount;
        std::cout << "ATM補(bǔ)充現(xiàn)金: " << amount << ",當(dāng)前總現(xiàn)金: " << cash_available << std::endl;
    }
};
// 模擬用戶線程
void user_thread(ATMSystem& atm, int user_id) {
    std::cout << "用戶 " << user_id << " 嘗試取款..." << std::endl;
    atm.withdraw(100);
}
// 模擬維護(hù)線程
void maintenance_thread(ATMSystem& atm) {
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    atm.start_maintenance();
    // 執(zhí)行維護(hù)操作
    std::this_thread::sleep_for(std::chrono::milliseconds(300));
    atm.refill_cash(500);
    // 維護(hù)結(jié)束
    atm.end_maintenance();
}
int main() {
    ATMSystem atm(300);  // 初始現(xiàn)金300元
    // 啟動(dòng)一個(gè)維護(hù)線程和多個(gè)用戶線程
    std::thread maint(maintenance_thread, std::ref(atm));
    std::vector<std::thread> users;
    for (int i = 1; i <= 5; ++i) {
        users.push_back(std::thread(user_thread, std::ref(atm), i));
    }
    // 等待所有線程結(jié)束
    maint.join();
    for (auto& t : users) {
        t.join();
    }
    return 0;
}

總結(jié)

  • mutex:最基礎(chǔ)的鎖,需要手動(dòng)鎖定和解鎖,用不好容易出問題,就像自己管理衛(wèi)生間門鎖。
  • lock_guard:簡單安全的自動(dòng)鎖,構(gòu)造時(shí)鎖定,析構(gòu)時(shí)解鎖,但不能中途操作鎖狀態(tài),就像請了個(gè)死板但可靠的保安。
  • unique_lock:功能最全面的鎖包裝器,靈活性最高,但有輕微的性能開銷,就像一個(gè)萬能的管家。

最佳實(shí)踐

  • 簡單場景,優(yōu)先使用lock_guard
  • 需要條件變量或靈活鎖定/解鎖時(shí),使用unique_lock
  • 對性能極度敏感的場景,考慮直接使用mutex,但要非常小心

希望這篇文章能讓你對C++11的同步工具有個(gè)清晰的認(rèn)識(shí)。多線程不再可怕,熟練掌握這"三兄弟",你就能寫出安全高效的并發(fā)程序啦!

到此這篇關(guān)于C++11鎖機(jī)制三兄弟大比拼:mutex、lock_guard與unique_lock的文章就介紹到這了,更多相關(guān)C++ mutex、lock_guard與unique_lock內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于C語言實(shí)現(xiàn)簡單的走迷宮游戲

    基于C語言實(shí)現(xiàn)簡單的走迷宮游戲

    這篇文章主要介紹了基于C語言實(shí)現(xiàn)簡單的走迷宮游戲,用到雙向隊(duì)列,方便在運(yùn)行完畢后輸出經(jīng)過的點(diǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • 一文帶你了解C語言中的0長度數(shù)組(可變數(shù)組/柔性數(shù)組)

    一文帶你了解C語言中的0長度數(shù)組(可變數(shù)組/柔性數(shù)組)

    眾所周知,?GNU/GCC?在標(biāo)準(zhǔn)的?C/C++?基礎(chǔ)上做了有實(shí)用性的擴(kuò)展,?零長度數(shù)組(Arrays?of?Length?Zero)?就是其中一個(gè)知名的擴(kuò)展,本文就來聊聊零長度數(shù)組的相關(guān)知識(shí)吧
    2023-03-03
  • C語言實(shí)現(xiàn)斗地主的核心算法

    C語言實(shí)現(xiàn)斗地主的核心算法

    本文給大家分享的是使用C語言實(shí)現(xiàn)的斗地主游戲的核心算法,主要實(shí)現(xiàn)了面向?qū)ο笤O(shè)計(jì),洗牌、發(fā)牌、判斷牌型、比較牌的大小、游戲規(guī)則等算法。通過這個(gè)斗地主小項(xiàng)目的練習(xí),提高了我的面向?qū)ο笤O(shè)計(jì)能力,加深了對算法的理解。最近把這些設(shè)計(jì)和算法分享給大家。
    2015-03-03
  • C語言數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)

    C語言數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)

    鏈表是一種物理存儲(chǔ)結(jié)構(gòu)上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的。本文將用C語言實(shí)現(xiàn)單鏈表,需要的可以參考一下
    2022-06-06
  • C語言實(shí)現(xiàn)簡易的三子棋小游戲

    C語言實(shí)現(xiàn)簡易的三子棋小游戲

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)簡易的三子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Qt5.9程序打包發(fā)布的實(shí)現(xiàn)

    Qt5.9程序打包發(fā)布的實(shí)現(xiàn)

    本文主要介紹了Qt5.9程序打包發(fā)布的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • OpenCV實(shí)現(xiàn)圖像細(xì)化算法

    OpenCV實(shí)現(xiàn)圖像細(xì)化算法

    本文主要介紹了OpenCV實(shí)現(xiàn)圖像細(xì)化算法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • C語言 深入淺出講解指針的使用

    C語言 深入淺出講解指針的使用

    指針是C語言中一個(gè)非常重要的概念,也是C語言的特色之一。使用指針可以對復(fù)雜數(shù)據(jù)進(jìn)行處理,能對計(jì)算機(jī)的內(nèi)存分配進(jìn)行控制,在函數(shù)調(diào)用中使用指針還可以返回多個(gè)值
    2022-03-03
  • MFC串口通信發(fā)送16進(jìn)制數(shù)據(jù)的方法

    MFC串口通信發(fā)送16進(jìn)制數(shù)據(jù)的方法

    這篇文章主要為大家詳細(xì)介紹了MFC串口通信發(fā)送16進(jìn)制數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • Cocos2d-x中使用CCScrollView來實(shí)現(xiàn)關(guān)卡選擇實(shí)例

    Cocos2d-x中使用CCScrollView來實(shí)現(xiàn)關(guān)卡選擇實(shí)例

    這篇文章主要介紹了Cocos2d-x中使用CCScrollView來實(shí)現(xiàn)關(guān)卡的選擇實(shí)例,本文在代碼中用大量注釋講解了CCScrollView的使用,需要的朋友可以參考下
    2014-09-09

最新評論