自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

C++并發(fā)編程簡史:一段你不得不知道的傳奇

開發(fā)
C++17 和 C++20 更是錦上添花,帶來了shared_mutex、協(xié)程等炫酷特性 ???纯船F(xiàn)在的 C++,簡直就像變了個人似的,讓我們能優(yōu)雅地駕馭多核時代的浪潮!

嘿,讓我們來聊聊 C++ 并發(fā)編程的精彩旅程吧!?? 想象一下,在 1998 年那個"單線程時代",C++ 就像個固執(zhí)的獨行俠,完全不懂多線程的魅力。開發(fā)者們只能依賴各種系統(tǒng)專屬的 API,就像在用方言交流一樣難懂 ??。

后來,像 Boost.Thread 這樣的"翻譯官"??? 出現(xiàn)了,讓并發(fā)編程變得優(yōu)雅了不少。但真正的轉(zhuǎn)折點是 2011 年的 C++11 標準,它給 C++ 裝上了"并發(fā)引擎"???,帶來了std::thread、std::mutex 等超強工具。

C++17 和 C++20 更是錦上添花,帶來了shared_mutex、協(xié)程等炫酷特性 ???纯船F(xiàn)在的 C++,簡直就像變了個人似的,讓我們能優(yōu)雅地駕馭多核時代的浪潮!??

C++多線程的前世今生

啊,讓我們坐上時光機,回到1998年 ?。那時的C++還是個"單線程主義者" - 它根本不承認多線程的存在!就像一個固執(zhí)的老頑固,堅持"一次只做一件事"。不僅如此,它連個像樣的內(nèi)存模型都沒有。程序員們想寫多線程程序?抱歉,除非你愿意依賴編譯器特定的擴展... ??

但是呢,編譯器廠商們可不這么想。他們看到了程序員們渴望多線程的眼神 ??。于是乎,他們開始通過各種方式支持多線程。Windows程序員有了Windows API,Unix黨有了POSIX線程庫。雖然這些支持比較基礎(基本就是把C的API搬過來),但是好歹能用不是? ??

來看個具體例子。假設我們想寫個簡單的多線程程序,在Windows下可能是這樣:

#include <windows.h>
#include <stdio.h>

// ?? 線程函數(shù) - 就像一個獨立的小工人
DWORD WINAPI PrintHello(LPVOID lpParam) {
    printf("Hello from Windows thread!\n");  // ?? 打個招呼
    return0;  // ? 工作完成,安全退出
}

int main() {
    // ?? 創(chuàng)建新線程 - 就像開啟一條新的生產(chǎn)線
    HANDLE hThread = CreateThread(
        NULL,       // ?? 默認安全屬性
        0,         // ?? 默認棧大小
        PrintHello, // ???? 指定工人要做的工作
        NULL,       // ?? 沒有參數(shù)傳遞
        0,         // ?? 立即啟動線程
        NULL        // ??? 不需要線程ID
    );
    
    // ? 等待線程完成 - 像等待工人完成工作
    WaitForSingleObject(hThread, INFINITE);
    
    // ?? 清理線程句柄 - 收拾好工作臺
    CloseHandle(hThread);
    return0;  // ?? 主程序圓滿完成
}

而在POSIX系統(tǒng)上,同樣的程序要這么寫:

#include <pthread.h>  // ?? POSIX 線程庫頭文件
#include <stdio.h>    // ?? 標準輸入輸出

// ?? 線程執(zhí)行函數(shù) - 就像一個獨立的工作者
void* PrintHello(void* arg) {
    printf("Hello from POSIX thread!\n");  // ?? 打個招呼
    returnNULL;  // ? 工作完成,安全返回
}

int main() {
    pthread_t thread;  // ?? 聲明線程變量,像是工人的工牌

    // ?? 創(chuàng)建并啟動新線程
    // 參數(shù)分別是:
    // 1?? 線程標識符的指針
    // 2?? 線程屬性(NULL表示使用默認屬性)
    // 3?? 線程將要執(zhí)行的函數(shù)
    // 4?? 傳遞給線程函數(shù)的參數(shù)
    pthread_create(&thread, NULL, PrintHello, NULL);

    // ? 等待線程完成 - 就像等待工人干完活
    pthread_join(thread, NULL);
    
    return0;  // ?? 主程序圓滿結(jié)束
}

看到?jīng)]? 同樣的功能,兩種完全不同的寫法! 這簡直就像是在寫兩種不同的語言。

但是C++程序員們可不滿足于此。他們想要更優(yōu)雅的解決方案! 于是像MFC、Boost這樣的庫橫空出世了 ??♂?。這些庫把底層的API包裝得漂漂亮亮的,還加入了很多實用的功能。比如說,使用Boost.Thread的話,上面的代碼就可以寫成這樣:

#include <boost/thread.hpp>    // ?? Boost的線程庫
#include <iostream>           // ?? 輸入輸出流

// ?? 線程要執(zhí)行的任務函數(shù)
void PrintHello() {
    std::cout << "Hello from Boost thread!" << std::endl;  // ?? 打個招呼
}

int main() {
    // ?? 創(chuàng)建并啟動新線程
    boost::thread t(PrintHello);  // ?? 線程開始執(zhí)行
    
    t.join();  // ? 等待線程完成
    return0;  // ?? 主程序結(jié)束
}

是不是清爽多了? ?? 特別是它引入了RAII(資源獲取即初始化)的概念,讓互斥鎖的使用變得更安全。就像這樣:

boost::mutex mtx;  // ?? 創(chuàng)建一個互斥鎖 - 就像一把神奇的鎖
{
    // ?? RAII方式加鎖 - 進入?yún)^(qū)域自動上鎖
    boost::mutex::scoped_lock lock(mtx);  
    
    // ?? 這里是受保護的代碼區(qū)域
    // ?? 只有一個線程能在同一時間進入這里
    // ?? 可以安全地訪問共享資源
    // ?? 比如修改共享數(shù)據(jù)...
    
} // ?? 離開作用域時自動解鎖 - 非常優(yōu)雅且安全!
  // ??? 即使發(fā)生異常也能確保解鎖

但是呢,這些庫再好,終究不是語言標準的一部分??缙脚_時還是會遇到各種奇怪的問題 ??。直到C++11的出現(xiàn),這個問題才得到了徹底的解決。但這個,就是另外一個精彩的故事了... ?

這段歷史告訴我們什么呢?它讓我們看到了C++社區(qū)的創(chuàng)造力和適應力。即使在標準不支持的情況下,依然找到了方法來滿足多線程編程的需求。這種精神,才是真正讓C++成為一門偉大語言的原因啊! ??

C++11: 多線程的春天來了! 

2011年,C++終于迎來了期待已久的官方多線程支持! 就像給C++裝上了一臺"并發(fā)引擎" ???。來看看這個全新的世界吧:

#include <iostream>     // ?? 標準輸入輸出流
#include <thread>       // ?? 線程支持
#include <mutex>        // ?? 互斥鎖支持

// ??? 保護std::cout的互斥鎖,防止輸出混亂
std::mutex cout_mutex;  

// ???? 咖啡師的工作流程
void MakeCoffee() {
    std::lock_guard<std::mutex> lock(cout_mutex);  // ?? 自動加鎖解鎖,很安全!
    std::cout << "? 正在煮咖啡..." << std::endl;  // ?? 安全地輸出信息
}

// ???? 茶師的工作流程
void MakeTea() {
    std::lock_guard<std::mutex> lock(cout_mutex);  // ?? 獲取輸出權限
    std::cout << "?? 正在泡茶..." << std::endl;    // ?? 安全地輸出信息
}

int main() {
    // ?? 開啟兩條并行的工作流水線
    std::thread coffee_master(MakeCoffee);    // ?? 啟動咖啡師的線程
    std::thread tea_master(MakeTea);          // ?? 啟動茶師的線程
    
    // ? 等待兩位師傅完成他們的工作
    coffee_master.join();    // ?? 等待咖啡師
    tea_master.join();       // ?? 等待茶師
    
    return0;  // ?? 圓滿完成!
}

看到了嗎?這就是現(xiàn)代C++的魅力! ?? 不需要再記那些晦澀的API了,一切都變得如此自然。就像用中文寫代碼一樣順暢~

而且C++11不只是提供了基礎的線程支持,它還給了我們一整套并發(fā)工具箱! 

比如說,如果我們想讓咖啡師和茶師輪流工作,可以用條件變量:

首先是基本的頭文件和全局變量設置:

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

// ?? 我們需要這些工具來協(xié)調(diào)咖啡師和茶師的工作
std::mutex mtx;                    // ?? 互斥鎖:就像休息室的門鎖
std::condition_variable cv;        // ?? 條件變量:像是咖啡師和茶師之間的對講機
bool coffee_ready = false;         // ?? 狀態(tài)標志:咖啡完成的信號

接下來是咖啡師的工作流程:

void BaristaMakeCoffee() {
    // 第一步:宣布開始工作 ??
    {
        std::lock_guard<std::mutex> lock(mtx);    // ?? 先鎖門
        std::cout << "咖啡師: 開始煮咖啡..." << std::endl;
    }   // ?? 自動解鎖,其他人可以用輸出了

    // 第二步:認真煮咖啡 ??
    std::this_thread::sleep_for(std::chrono::seconds(2));  // ? 煮咖啡需要時間

    // 第三步:完成并通知茶師 ??
    {
        std::lock_guard<std::mutex> lock(mtx);
        coffee_ready = true;   // ?? 設置完成標志
        std::cout << "咖啡師: 咖啡準備好了! ??" << std::endl;
    }
    cv.notify_one();  // ?? 給茶師發(fā)消息
}

然后是茶師的工作流程:

void TeaMasterWaitAndMakeTea() {
    // 第一步:等待咖啡師的信號 ??
    {
        std::unique_lock<std::mutex> lock(mtx);   // ?? 特殊的鎖,可以被條件變量解開
        cv.wait(lock, [] { return coffee_ready; });  // ?? 等待咖啡完成信號
    }

    // 第二步:開始泡茶 ??
    {
        std::lock_guard<std::mutex> lock(mtx);
        std::cout << "茶師: 咖啡好了,我開始泡茶..." << std::endl;
    }

    std::this_thread::sleep_for(std::chrono::seconds(1));  // ?? 泡茶也需要時間

    // 第三步:完成泡茶 ?
    {
        std::lock_guard<std::mutex> lock(mtx);
        std::cout << "茶師: 茶也準備好了! ??" << std::endl;
    }
}

最后是主程序的協(xié)調(diào)部分:

int main() {
    std::cout << "?? 咖啡廳開始營業(yè)..." << std::endl;

    // ?? 安排兩位師傅開始工作
    std::thread barista(BaristaMakeCoffee);       // ???? 咖啡師上崗
    std::thread tea_master(TeaMasterWaitAndMakeTea); // ???? 茶師上崗

    // ?? 等待工作完成
    barista.join();    // ?? 等咖啡師完成
    tea_master.join(); // ?? 等茶師完成

    std::cout << "?? 今天的飲品都準備完成啦!" << std::endl;
    return 0;

這不就是現(xiàn)實生活中的場景嗎? 茶師要等咖啡師完成才開始工作,多么和諧的工作流程! ??

  • C++11還引入了很多其他好用的工具:
  • std::async 和std::future 用于異步任務 ??
  • std::atomic 用于原子操作 ??

各種同步原語(互斥鎖、條件變量、信號量等) ??

最重要的是,這些工具都是標準庫的一部分,意味著你的代碼可以在任何支持C++11的平臺上運行! 再也不用擔心跨平臺問題了~ ??

C++17與C++20: 并發(fā)編程的新篇章

哇!讓我們一起來看看C++17和C++20在并發(fā)編程方面帶來的超級大禮包吧! 

還記得以前處理多個互斥鎖時那種提心吊膽的感覺嗎?生怕搞出死鎖來 ???,F(xiàn)在好啦!C++17給我們帶來了超級實用的scoped_lock!它就像一個聰明的管家 ??,自動幫我們按照正確的順序處理多個鎖,再也不用擔心死鎖啦!

// 看看這個超級管家是怎么工作的 ??
std::mutex m1, m2, m3;  // 三把小鎖 ??
{
    std::scoped_lock locks(m1, m2, m3);  // 交給管家,一切都搞定!
    // 安心寫代碼... ??
}  // 管家會自動幫我們解鎖,貼心! ?

C++17還給我們帶來了shared_mutex - 這簡直就是給讀寫操作開了個派對! ?? 多個讀者可以一起蹦迪,但寫者需要包場獨舞~ 這不就是傳說中的"共享-獨占"模式嘛!

std::shared_mutex party_room; ??
// 讀者們可以一起嗨! ????
{
    std::shared_lock<std::shared_mutex> group_entry(party_room);
    // 大家一起讀數(shù)據(jù),熱鬧! ??
}
// 寫者需要包場 ??
{
    std::unique_lock<std::shared_mutex> vip_entry(party_room);
    // 獨自修改數(shù)據(jù),安靜... ??
}

到了C++20,簡直就是開了掛! ?? 它帶來了jthread(智能線程)、閉鎖、屏障、信號量這些厲害角色!特別是jthread,它就像是給普通線程裝上了自動駕駛系統(tǒng) ??,不用手動join,還能隨時喊停!

// 來看看這個智能線程有多聰明 ??
void future_threads() {
    std::jthread smart_worker([](std::stop_token stoken) {
        while (!stoken.stop_requested()) {  // 隨時準備停車! ??
            // 干活ing... ????
        }
    });
    // 不用管它,下班自己會收工! ??
}

最讓人興奮的是協(xié)程的加入! ?? 它就像給你的代碼加上了任意門,可以隨時暫停、繼續(xù),玩出各種花樣!比如這個生成斐波那契數(shù)列的協(xié)程,簡直優(yōu)雅得不要不要的~ ?

generator<int> fibonacci() {  // 數(shù)學界的魔術師 ??
    int a = 0, b = 1;
    while (true) {
        co_yield a;  // 變個魔術,產(chǎn)生下一個數(shù)! ?
        auto tmp = a;
        a = b;
        b = tmp + b;
    }
}

有了這些強大的新工具,寫并發(fā)代碼簡直就像在玩積木一樣有趣! ?? 再也不用被那些繁瑣的同步問題困擾啦!讓我們一起擁抱這個多線程的新時代吧! ??

性能與調(diào)試提示

嘿,小伙伴們!寫多線程代碼時,最容易掉進的坑就是"線程越多越好"的誤區(qū)啦!?? 這就像開派對一樣,人多不一定熱鬧,可能反而會踩踩踩!

來看個常見的"踩坑"案例:

// ? 不推薦
void process_items(const std::vector<Item>& items) {
    std::vector<std::thread> threads;
    // 每個任務都開一個新線程,CPU: 我太難了! ??
    for (constauto& item : items) {
        threads.emplace_back([&item]{ process(item); });
    }
}

// ? 推薦
void process_items(const std::vector<Item>& items) {
    // 讓CPU告訴我們它能同時處理多少線程 ??
    constauto thread_count = std::thread::hardware_concurrency();
    ThreadPool pool(thread_count);  // 建個溫馨的線程小家庭 ??
    
    // 往線程池丟任務,它自己會安排得明明白白的 ??
    for (constauto& item : items) {
        pool.enqueue([&item]{ process(item); });
    }
}

還有個省心小技巧:如果只是想給個數(shù)字加加減減,用原子操作就夠啦!?? 就像點外賣,一個人點完全程序,比叫一群人一起點要順暢多了:

// 這個計數(shù)器特別乖,不用加鎖也不會亂 ??
std::atomic<int> counter{0};  
counter++;  // 一個頂一個,穩(wěn)得很!??

記住啦:線程不是越多越好,原子操作不是越多越妙,關鍵是要用對地方!就像調(diào)味料一樣,適量才能讓代碼更美味~ ??

寫并發(fā)代碼就是這樣,與其把時間花在處理復雜的同步問題上,不如好好想想怎么讓架構(gòu)更簡單!畢竟,能用一把鎖解決的問題,干嘛要用兩把呢???

責任編輯:趙寧寧 來源: everystep
相關推薦

2020-10-21 09:36:40

Vue項目技巧

2022-08-30 23:54:42

MySQL數(shù)據(jù)庫工具

2022-10-27 09:55:00

2017-08-10 16:54:47

MySQL優(yōu)化MySQL

2020-02-13 18:05:18

數(shù)組reduce前端

2023-08-29 08:41:42

2015-09-22 10:03:25

大數(shù)據(jù)秘訣

2022-08-08 11:13:35

API接口前端

2015-09-23 10:27:04

大數(shù)據(jù)秘訣

2017-11-02 06:51:38

5G移動網(wǎng)絡技術

2020-09-02 07:30:31

前端Vue.set代碼

2019-07-17 10:55:40

Kubernetes工具Katacoda

2017-08-16 18:03:12

Docker安全工具容器

2020-05-18 09:33:27

前端開發(fā)工具

2009-11-11 16:48:29

Visual C++

2009-05-31 09:02:23

2017-09-22 09:10:41

2010-08-27 10:40:55

Android

2010-08-18 11:36:40

DB2簡史

2011-03-31 10:46:54

LinuxCLI軟件
點贊
收藏

51CTO技術棧公眾號