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

C++11 條件變量到底有多強(qiáng)?五分鐘帶你徹底搞懂線程同步!

開(kāi)發(fā)
看完這篇文章,保證你對(duì)C++條件變量有種"哦,原來(lái)如此!"的頓悟感。不信?往下看就知道了!

大家好啊,我是小康。今天咱們聊一個(gè)聽(tīng)起來(lái)挺高深,但其實(shí)超實(shí)用的話題 —— C++11條件變量。

說(shuō)實(shí)話,我第一次接觸這玩意兒時(shí)也是一臉懵逼:"條件變量?這不就是個(gè)變量嗎,有啥好講的?"

結(jié)果一看代碼,頓時(shí)傻眼了...

但別慌!今天我用最白話的方式幫你徹底搞懂它。不講那些晦澀的概念,就講你真正需要知道的東西。

一、條件變量到底是個(gè)啥?

想象你和朋友在肯德基排隊(duì),但你突然想上廁所。

你對(duì)朋友說(shuō):"哥們,我去個(gè)衛(wèi)生間,到咱們了你喊我一聲啊!"

然后你去衛(wèi)生間了,但并不是一直站在那兒傻等,而是該干嘛干嘛去了。

這就是條件變量的核心思想:一個(gè)線程(你)在等待某個(gè)條件滿足(隊(duì)排到了),另一個(gè)線程(你朋友)負(fù)責(zé)在條件滿足時(shí)通知等待的線程(你)。

條件變量的厲害之處就是:它讓等待的線程能夠暫時(shí)"睡眠",不消耗CPU資源,直到被另一個(gè)線程喚醒。

二、為啥要用條件變量?

直接上一個(gè)生活中的例子:

假設(shè)你在煮方便面,說(shuō)好了3分鐘熟。你有兩種等待方式:

  • 傻等法:眼睛死盯著鍋和手表,不停地問(wèn)自己"好了沒(méi)?好了沒(méi)?"(這就是所謂的"忙等待",特別浪費(fèi)資源)
  • 聰明等法:設(shè)個(gè)3分鐘鬧鐘,然后玩會(huì)手機(jī),鬧鈴響了再去看鍋(這就是條件變量的思想)

顯然,聰明等法更高效,既不浪費(fèi)你的注意力(CPU資源),事情也能圓滿完成。

三、條件變量的基本用法

C++11中,我們主要用到這兩個(gè)類:

  • std::condition_variable - 就是我們的條件變量主角
  • std::mutex - 它的好搭檔,互斥鎖

基本用法分 2 步:

  • 等待條件滿足(等待方)
std::unique_lock<std::mutex> lock(mutex); // 先上鎖
while (!條件滿足) {  // 檢查條件
    cv.wait(lock);   // 不滿足就等待(自動(dòng)釋放鎖并休眠)
}
// 條件滿足了,繼續(xù)執(zhí)行
// 鎖還在手里,記得用完放開(kāi)
  • 滿足條件并通知(通知方)
{
    std::lock_guard<std::mutex> lock(mutex); // 先上鎖
    // 改變條件狀態(tài)
    條件 = true;
} // 鎖自動(dòng)釋放

cv.notify_one(); // 通知一個(gè)等待的線程
// 或
cv.notify_all(); // 通知所有等待的線程

就這么簡(jiǎn)單!

但是,光說(shuō)不練假把式,來(lái)看個(gè)具體例子。

四、經(jīng)典案例:生產(chǎn)者-消費(fèi)者問(wèn)題

我們用做早餐來(lái)解釋:

  • 生產(chǎn)者:就是做煎餅的師傅(不斷地生產(chǎn)煎餅)
  • 消費(fèi)者:就是饑腸轆轆的食客(不斷地吃煎餅)
  • 共享緩沖區(qū):就是放煎餅的托盤(容量有限)

規(guī)則很簡(jiǎn)單:

  • 托盤滿了,師傅就得等等(生產(chǎn)者等待)
  • 托盤空了,食客就得等等(消費(fèi)者等待)
  • 師傅做好一個(gè),告訴食客可以吃了(生產(chǎn)者通知)
  • 食客吃完一個(gè),告訴師傅可以繼續(xù)做了(消費(fèi)者通知)

代碼實(shí)現(xiàn):

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>

using namespace std;

// 共享數(shù)據(jù)及同步對(duì)象
queue<int> products;         // 煎餅托盤
mutex mtx;                   // 互斥鎖
condition_variable cv_empty;  // 托盤空了的條件變量
condition_variable cv_full;   // 托盤滿了的條件變量
constint MAX_PRODUCTS = 5;   // 托盤最多放5個(gè)煎餅

// 生產(chǎn)者線程(做煎餅的師傅)
void producer() {
    for (int i = 1; i <= 10; ++i) {  // 做10個(gè)煎餅
        {
            unique_lock<mutex> lock(mtx);  // 先上鎖

            // 如果托盤滿了,就等待
            cv_empty.wait(lock, []{
                return products.size() < MAX_PRODUCTS;
            });

            // 做一個(gè)煎餅,放到托盤上
            products.push(i);
            cout << "師傅做好第 " << i << " 個(gè)煎餅,托盤現(xiàn)在有 "
                << products.size() << " 個(gè)煎餅\n";
        } // 解鎖

        // 通知消費(fèi)者有煎餅可以吃了
        cv_full.notify_one();

        // 做煎餅需要一點(diǎn)時(shí)間
        this_thread::sleep_for(chrono::milliseconds(300));
    }
}

// 消費(fèi)者線程(吃煎餅的食客)
void consumer() {
    for (int i = 1; i <= 10; ++i) {  // 要吃10個(gè)煎餅
        int product;
        {
            unique_lock<mutex> lock(mtx);  // 先上鎖

            // 如果托盤空了,就等待
            cv_full.wait(lock, []{
                return !products.empty();
            });

            // 從托盤取一個(gè)煎餅吃
            product = products.front();
            products.pop();
            cout << "食客吃掉第 " << product << " 個(gè)煎餅,托盤還剩 "
                << products.size() << " 個(gè)煎餅\n";
        } // 解鎖

        // 通知生產(chǎn)者托盤有空位了
        cv_empty.notify_one();

        // 吃煎餅需要一點(diǎn)時(shí)間
        this_thread::sleep_for(chrono::milliseconds(500));
    }
}

int main() {
    cout << "===== 煎餅店開(kāi)張啦! =====\n";

    thread t1(producer);  // 啟動(dòng)生產(chǎn)者線程
    thread t2(consumer);  // 啟動(dòng)消費(fèi)者線程

    t1.join();  // 等待生產(chǎn)者線程結(jié)束
    t2.join();  // 等待消費(fèi)者線程結(jié)束

    cout << "===== 煎餅賣完了! =====\n";
    return 0;
}

運(yùn)行結(jié)果可能是這樣的:

===== 煎餅店開(kāi)張啦! =====
師傅做好第 1 個(gè)煎餅,托盤現(xiàn)在有 1 個(gè)煎餅
食客吃掉第 1 個(gè)煎餅,托盤還剩 0 個(gè)煎餅
師傅做好第 2 個(gè)煎餅,托盤現(xiàn)在有 1 個(gè)煎餅
師傅做好第 3 個(gè)煎餅,托盤現(xiàn)在有 2 個(gè)煎餅
食客吃掉第 2 個(gè)煎餅,托盤還剩 1 個(gè)煎餅
師傅做好第 4 個(gè)煎餅,托盤現(xiàn)在有 2 個(gè)煎餅
食客吃掉第 3 個(gè)煎餅,托盤還剩 1 個(gè)煎餅
師傅做好第 5 個(gè)煎餅,托盤現(xiàn)在有 2 個(gè)煎餅
食客吃掉第 4 個(gè)煎餅,托盤還剩 1 個(gè)煎餅
師傅做好第 6 個(gè)煎餅,托盤現(xiàn)在有 2 個(gè)煎餅
食客吃掉第 5 個(gè)煎餅,托盤還剩 1 個(gè)煎餅
師傅做好第 7 個(gè)煎餅,托盤現(xiàn)在有 2 個(gè)煎餅
食客吃掉第 6 個(gè)煎餅,托盤還剩 1 個(gè)煎餅
師傅做好第 8 個(gè)煎餅,托盤現(xiàn)在有 2 個(gè)煎餅
食客吃掉第 7 個(gè)煎餅,托盤還剩 1 個(gè)煎餅
師傅做好第 9 個(gè)煎餅,托盤現(xiàn)在有 2 個(gè)煎餅
食客吃掉第 8 個(gè)煎餅,托盤還剩 1 個(gè)煎餅
師傅做好第 10 個(gè)煎餅,托盤現(xiàn)在有 2 個(gè)煎餅
食客吃掉第 9 個(gè)煎餅,托盤還剩 1 個(gè)煎餅
食客吃掉第 10 個(gè)煎餅,托盤還剩 0 個(gè)煎餅
===== 煎餅賣完了! =====

看到?jīng)]?師傅和食客配合得多默契啊!這就是條件變量的魅力:讓兩個(gè)線程之間能夠無(wú)縫協(xié)作。

五、條件變量的幾個(gè)關(guān)鍵點(diǎn)

1. 為什么要用 while 循環(huán)檢查條件?

也許你注意到了,示例代碼用的是 lambda 函數(shù)而不是 while 循環(huán)。但在老式寫法中,我們通常這樣:

while (!條件滿足) {
    cv.wait(lock);
}

不用 if 而用 while 的原因是:虛假喚醒。

有時(shí)候,等待的線程可能會(huì)在沒(méi)有人通知的情況下醒來(lái)(就像你睡覺(jué)時(shí)突然被樓上裝修吵醒)。如果用 if,線程會(huì)錯(cuò)誤地認(rèn)為條件已滿足;用 while,它會(huì)再檢查一遍,發(fā)現(xiàn)條件沒(méi)滿足,繼續(xù)等待。

2. wait() 的兩種用法

條件變量的 wait() 有兩種調(diào)用方式:

// 方式1:只傳遞鎖
cv.wait(lock);

// 方式2:傳遞鎖和判斷條件(推薦)
cv.wait(lock, []{ return 條件滿足; });

方式 2 相當(dāng)于:

while (!條件滿足) {
    cv.wait(lock);
}

但方式 2 更簡(jiǎn)潔、更不容易出錯(cuò),強(qiáng)烈推薦使用!

3. 重要的超時(shí)等待函數(shù)

有時(shí)候,我們不想無(wú)限期等待,而是最多等待一段時(shí)間。C++11提供了超時(shí)版本的 wait 函數(shù):

// 最多等待100毫秒
auto status = cv.wait_for(lock, chrono::milliseconds(100), []{ return 條件滿足; });

if (status) {
    // 條件滿足了
} else {
    // 超時(shí)了,條件仍未滿足
}

這就像你等外賣:如果 30 分鐘送不到,我就自己做飯吃了!

六、高級(jí)案例:線程池中的任務(wù)調(diào)度

想象一個(gè)更復(fù)雜的例子:一個(gè)簡(jiǎn)單的線程池。這是很多高性能系統(tǒng)的基礎(chǔ)設(shè)施:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <functional>

using namespace std;

class ThreadPool {
private:
vector<thread> workers;          // 工作線程
queue<function<void()>> tasks;   // 任務(wù)隊(duì)列

mutex mtx;                       // 互斥鎖
condition_variable cv;           // 條件變量
bool stop;                       // 停止標(biāo)志

public:
// 構(gòu)造函數(shù),創(chuàng)建指定數(shù)量的工作線程
ThreadPool(size_t threads) : stop(false) {
    for (size_t i = 0; i < threads; ++i) {
        workers.emplace_back([this] {
            while (true) {
                function<void()> task;

                {
                    unique_lock<mutex> lock(this->mtx);

                    // 等待任務(wù)或停止信號(hào)
                    this->cv.wait(lock, [this] {
                        returnthis->stop || !this->tasks.empty();
                    });

                    // 如果線程池停止且沒(méi)有任務(wù),則退出
                    if (this->stop && this->tasks.empty()) {
                        return;
                    }

                    // 獲取一個(gè)任務(wù)
                    task = move(this->tasks.front());
                    this->tasks.pop();
                }

                // 執(zhí)行任務(wù)
                task();
            }
        });
    }
}

// 添加新任務(wù)到線程池
template<class F>
void enqueue(F&& f) {
    {
        unique_lock<mutex> lock(mtx);

        // 不允許在線程池停止后添加任務(wù)
        if (stop) {
            throw runtime_error("ThreadPool已停止,無(wú)法添加任務(wù)");
        }

        tasks.emplace(forward<F>(f));
    }

    // 通知一個(gè)等待的線程有新任務(wù)
    cv.notify_one();
}

// 析構(gòu)函數(shù),停止所有線程
~ThreadPool() {
    {
        unique_lock<mutex> lock(mtx);
        stop = true;
    }

    // 通知所有等待的線程
    cv.notify_all();

    // 等待所有線程結(jié)束
    for (auto& worker : workers) {
        worker.join();
    }
}
};

// 測(cè)試線程池
int main() {
    // 創(chuàng)建4個(gè)工作線程的線程池
    ThreadPool pool(4);

    // 添加一些任務(wù)
    for (int i = 1; i <= 8; ++i) {
        pool.enqueue([i] {
            cout << "任務(wù) " << i << " 開(kāi)始執(zhí)行,線程ID: "
                << this_thread::get_id() << endl;

            // 模擬任務(wù)執(zhí)行時(shí)間
            this_thread::sleep_for(chrono::seconds(1));

            cout << "任務(wù) " << i << " 執(zhí)行完成" << endl;
        });
    }

    // 主線程暫停一會(huì)兒,讓工作線程有時(shí)間執(zhí)行任務(wù)
    this_thread::sleep_for(chrono::seconds(10));

    cout << "主線程退出" << endl;
    return 0;
}

運(yùn)行結(jié)果可能是這樣的:

任務(wù) 1 開(kāi)始執(zhí)行,線程ID: 140271052129024
任務(wù) 2 開(kāi)始執(zhí)行,線程ID: 140271060521728
任務(wù) 3 開(kāi)始執(zhí)行,線程ID: 140271068914432
任務(wù) 4 開(kāi)始執(zhí)行,線程ID: 140271077307136
任務(wù) 1 執(zhí)行完成
任務(wù) 5 開(kāi)始執(zhí)行,線程ID: 140271052129024
任務(wù) 2 執(zhí)行完成
任務(wù) 6 開(kāi)始執(zhí)行,線程ID: 140271060521728
任務(wù) 3 執(zhí)行完成
任務(wù) 7 開(kāi)始執(zhí)行,線程ID: 140271068914432
任務(wù) 4 執(zhí)行完成
任務(wù) 8 開(kāi)始執(zhí)行,線程ID: 140271077307136
任務(wù) 5 執(zhí)行完成
任務(wù) 6 執(zhí)行完成
任務(wù) 7 執(zhí)行完成
任務(wù) 8 執(zhí)行完成
主線程退出

看!多個(gè)線程自動(dòng)分配任務(wù),互不干擾,效率杠杠的!

七、條件變量使用的注意事項(xiàng)

(1) 永遠(yuǎn)和互斥鎖一起使用:條件變量需要和互斥鎖配合,否則會(huì)導(dǎo)致競(jìng)態(tài)條件

(2) 檢查喚醒原因:被喚醒不一定是因?yàn)闂l件滿足,所以總是要檢查條件(用while或wait的謂詞版本)

(3) 注意通知時(shí)機(jī):通常先改變條件狀態(tài),再發(fā)出通知,且通知應(yīng)在解鎖后進(jìn)行

(4) 區(qū)分 notify_one 和 notify_all:

  • notify_one(): 只喚醒一個(gè)等待的線程(適合一對(duì)一通知)
  • notify_all(): 喚醒所有等待的線程(適合廣播通知)

(5) 防止丟失喚醒:如果通知在等待之前發(fā)出,那么可能會(huì)丟失,導(dǎo)致線程永遠(yuǎn)等待

八、總結(jié):條件變量,讓你的多線程程序更高效!

條件變量就像多線程世界里的"微信群通知":讓線程之間能夠高效協(xié)調(diào)工作,不必浪費(fèi)CPU資源去傻等。

關(guān)鍵知識(shí)點(diǎn)回顧:

  • 條件變量用于線程間的等待/通知機(jī)制
  • 必須與互斥鎖配合使用
  • 使用 wait() 等待條件滿足
  • 使用 notify_one()/notify_all() 通知等待的線程
  • 總是在循環(huán)中檢查條件,防止假喚醒

掌握了條件變量,你的C++多線程技能就上了一個(gè)臺(tái)階!再也不用擔(dān)心線程間如何優(yōu)雅地協(xié)作啦~

責(zé)任編輯:趙寧寧 來(lái)源: 跟著小康學(xué)編程
相關(guān)推薦

2021-06-18 07:34:12

Kafka中間件微服務(wù)

2025-03-13 06:22:59

2013-05-30 00:49:36

C++11C++條件變量

2013-12-11 10:00:14

C++新特性C

2024-12-11 07:00:00

面向?qū)ο?/a>代碼

2013-07-31 11:09:05

C++11

2025-01-21 07:39:04

Linux堆內(nèi)存Golang

2019-08-09 10:33:36

開(kāi)發(fā)技能代碼

2025-01-20 08:50:00

2021-10-19 07:27:08

HTTP代理網(wǎng)絡(luò)

2025-04-16 08:20:00

LinuxELF文件

2025-06-04 08:50:00

LambdaC++編程

2024-01-16 07:46:14

FutureTask接口用法

2019-11-04 11:30:51

區(qū)塊鏈技術(shù)智能

2025-01-24 08:38:47

2023-12-06 08:48:36

Kubernetes組件

2023-09-18 15:49:40

Ingress云原生Kubernetes

2024-01-12 07:38:38

AQS原理JUC

2024-07-05 09:31:37

點(diǎn)贊
收藏

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

主站蜘蛛池模板: 亚洲成人免费观看 | 日韩一区二区不卡 | 91精品91久久久 | 亚洲国产成人精品女人久久久野战 | 久草中文在线观看 | 日韩中文字幕一区二区 | 99久久久久久久久 | 亚洲三区视频 | 午夜免费电影院 | 男插女下体视频 | 91久久国产综合久久 | 亚洲欧美日韩在线不卡 | 成人免费影院 | 青青草精品视频 | 99精品视频免费在线观看 | 男人天堂手机在线视频 | 亚洲激情在线 | 国产精品久久久久久久久久久久午夜片 | 久久久久亚洲 | 久久久久国产精品 | 亚洲精品乱码久久久久久按摩观 | 狠狠操电影 | 国产亚洲精品久久久优势 | 99久久久久久久 | 亚洲欧美一区二区三区国产精品 | 日本不卡在线观看 | 欧美电影免费网站 | 欧美1页 | 日韩一区二区久久 | www久久| 午夜色播| 欧美亚洲国产一区二区三区 | 伊人婷婷| 亚洲+变态+欧美+另类+精品 | 亚洲国产精品一区二区久久 | 国产成人精品一区二 | 99精品国产一区二区三区 | 日韩在线免费播放 | 天堂在线www| 久久久久国产精品一区二区 | 亚洲天堂一区 |