4.c++語言級別的多線程編程

来源:https://www.cnblogs.com/woden3702/archive/2022/05/23/16303386.html
-Advertisement-
Play Games

泛型 前言 以前學習到「泛型」的時候,只是淺淺的知道可以限制類型,並沒有更深入理解,可以說基礎的也沒理解到位,只是浮於錶面,所以,現在回爐重造,重學泛型!打好基礎! 什麼是泛型? 泛型(Generic),Generic 的意思有「一般化的,通用的」。 是 JDK 5 中引入的新特性,它提供編譯時的類 ...


通過thread類編寫C++多線程程式

線程內容:

1、如何創建啟動一個線程?

​ std::thread定義一個線程對象,傳入線程所需的線程函數和參數,線程自動開啟

2、子線程如何結束?

​ 子線程函數運行完成,線程就結束了

3、主線程如何處理子線程

​ t.join() : 等待t線程結束,當前線程繼續往下運行

​ t.detach() : 把t線程設置為分離線程,主線程結束,整個進程結束,所有子線程都自動結束

#include <thread>

void threadHandler1(int time){
    std::this_thread::sleep_for(std::chrono::seconds(time));
    std::cout<<"call thread1"<<std::endl;
}

void threadHandler2(int time){
    std::this_thread::sleep_for(std::chrono::seconds(time));
    std::cout<<"call thread2"<<std::endl;
}

int main(){
    std::thread t1(threadHandler1,2);
    std::thread t2(threadHandler2,3);
    /*t1.join();
    t2.join();*/
    t1.detach();
    t2.join();
    std::cout<<"main thread done!"<<std::endl;
    return 0;
}
/*
列印輸出:
call thread1
call thread2
main thread done!
*/

線程間的互斥鎖mutex和lock_guard

上鎖常用lock_guard,他是對mutex的封裝,出作用域後會自動析構,析構的時候會釋放鎖,構造函數會上鎖

普通的互斥鎖是:

std::mutex mtx;
mtx.lock();
//臨界區
mtx.unlock();

使用lock_guard是這樣:

{
	std::lock_guard<std::mutex> lockGuard(mtx);//出作用域後會析構解鎖,預設構造是加鎖
	//臨界區
}

互斥鎖的應用:

//模擬買票
int ticketCount=100000;
std::mutex mtx;

void sellTicket(int index){
    while(ticketCount>0){//可能會發生提前進入迴圈的情況,所以需要在迴圈內再加一層判斷
//        mtx.lock();
        {//
            std::lock_guard<std::mutex> lockGuard(mtx);//出作用域後會析構解鎖,預設構造是加鎖
            if(ticketCount>0){
                //下麵兩句屬於臨界區代碼,該代碼段中的操作必須是原子操作,要保證線程之間要互斥。
                std::cout<<"視窗"<<index<<"賣出第"<<ticketCount<<"張票"<<std::endl;
                ticketCount--;
            }
        }
//        mtx.unlock();
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

int main(){
    std::list<std::thread> threadList;

    for(int i=1;i<=3;++i){
        threadList.push_back(std::thread(sellTicket,i));
    }

    for(auto &th:threadList){
        th.join();
    }

    std::cout<<"main thread done!"<<std::endl;
    return 0;
}

線程間的同步通訊機制

生產者,消費者線程模型

C++ STl中的所有容器都不是線程安全的

使用條件變數condition_variable做線程間的通信操作

線程間同步通信最典型的例子就是生產者-消費者模型,生產者線程生產出產品以後,會通知消費者線程去消費產品;如果消費者線程去消費產品,發現還沒有產品生產出來,它需要通知生產者線程趕快生產產品,等生產者線程生產出產品以後,消費者線程才能繼續往下執行。

C++11 線程庫提供的條件變數condition_variable,就是Linux平臺下的Condition Variable機制,用於解決線程間的同步通信問題,下麵通過代碼演示一個生產者-消費者線程模型,仔細分析代碼:

// 定義互斥鎖(條件變數需要和互斥鎖一起使用)
std::mutex mtx;
// 定義條件變數(用來做線程間的同步通信)
std::condition_variable cv;
// 定義vector容器,作為生產者和消費者共用的容器
std::vector<int> vec;

// 生產者線程函數
void producer()
{
    // 生產者每生產一個,就通知消費者消費一個
    for (int i = 1; i <= 10; ++i)
    {
        // 獲取mtx互斥鎖資源
        std::unique_lock<std::mutex> lock(mtx);

        // 如果容器不為空,代表還有產品未消費,等待消費者線程消費完,再生產
        while (!vec.empty())
        {
            // 判斷容器不為空,進入等待條件變數的狀態,釋放mtx鎖,
            // 讓消費者線程搶到鎖能夠去消費產品
            cv.wait(lock);
        }
        vec.push_back(i); // 表示生產者生產的產品序號i
        std::cout << "producer生產產品:" << i << std::endl;

        /*
         * 容器為空,說明需要生產,生產完對其他線程進行通訊
        生產者線程生產完產品,通知等待在cv條件變數上的消費者線程,
        可以開始消費產品了,然後釋放鎖mtx
        */
        cv.notify_all();

        // 生產一個產品,睡眠100ms
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}
// 消費者線程函數
void consumer()
{
    // 消費者每消費一個,就通知生產者生產一個
    for (int i = 1; i <= 10; ++i)
    {
        // 獲取mtx互斥鎖資源
        std::unique_lock<std::mutex> lock(mtx);

        // 如果容器為空,代表還有沒有產品可消費,等待生產者生產,再消費
        while (vec.empty())
        {
            // 判斷容器為空,進入等待條件變數的狀態,釋放mtx鎖,
            // 讓生產者線程搶到鎖能夠去生產產品
            cv.wait(lock);//在等待過程中如果收到信息就會進入阻塞狀態,執行下一步操作
        }
        int data = vec.back(); // 表示消費者消費的產品序號i
        vec.pop_back();
        std::cout << "consumer消費產品:" << data << std::endl;

        /*
        消費者消費完產品,通知等待在cv條件變數上的生產者線程,
        可以開始生產產品了,然後釋放鎖mtx
        */
        cv.notify_all();

        // 消費一個產品,睡眠100ms
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}
int main()
{
    // 創建生產者和消費者線程
    std::thread t1(producer);
    std::thread t2(consumer);

    // main主線程等待所有子線程執行完
    t1.join();
    t2.join();

    return 0;
}

再談lock_guard和unique_lock

互斥鎖只有一個線程可以拿到,其他線程如果試圖拿到被別的線程占用的互斥鎖的時候會進入阻塞狀態

std::lock_guard<std::mutex> lockGuard(mtx);不能用在函數參數傳遞或者返回過程中,只能用在簡單的臨界區代碼段的互斥操作中

unique_lock可以使用在簡單的臨界區代碼段的互斥操作中,也可以用在函數的調用過程中:

unque_lock<std::mutex> lck(mtx);
cv.wait(lck);//cv是condition_variable wait方法會先使線程進入等待狀態,然後調用lck.unlock()方法把mtx釋放掉。

cv.notify_all()是通知在cv上等待的線程條件成立了該幹活了,收到通知的線程會先從等待狀態切換到阻塞狀態,如果獲取到互斥鎖那麼該線程就會繼續執行。

基於CAS操作的atomic原子類型

首先要包含atomic類庫

一般在++等簡單操作中使用無鎖操作(CAS)來實現

//原子整型,CAS操作保證給count自增自減的原子操作
std::atomic_int mycount(0);

//線程函數
void sumTask()
{
    //每個線程給count加1000次
    for (int i = 0; i < 1000; ++i)
    {
        mycount++;
    }
}

int main()
{
    //創建10個線程放在容器當中
    std::vector<std::thread> vec;
    for (int i = 0; i < 10; ++i)
    {
        vec.push_back(std::thread(sumTask));
    }

    //等待線程執行完成
    for (unsigned int i = 0; i < vec.size(); ++i)
    {
        vec[i].join();
    }

    //所有子線程運行結束,count的結果每次運行應該都是10000
    std::cout << "count : " << mycount << std::endl;

    return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • **版本:**Android 9 **平臺:**RK-PX30 問題描述:在狀態欄點擊藍牙圖標打開藍牙時,發現打開失敗,進入到設置里打開藍牙卻能打開成功,但是時間會比較長。 分析: 查看日誌發現,執行BluetoothHci::initialize()後,在獲取到藍牙MAC地址,就又把藍牙關閉了。測 ...
  • 【導讀】 AR技術,是一種將真實世界信息和虛擬世界信息“無縫”銜接的技術,現如今AR技術受到日益廣泛的關註,在我們生活中發揮著重要的作用,並顯示出巨大的潛力……它是如何改變我們觀察世界的方式?本次直播,讓我們一起探索HMS Core AR Engine是如何幫助開發者們構築立體世界,打造沉浸式營銷的 ...
  • 前言 ​ SDK 需要把事件數據緩衝到本地,待符合一定策略再去同步數據。 一、數據存儲策略 ​ 在 iOS 應用程式中,從 “數據緩衝在哪裡” 這個緯度看,緩衝一般分兩種類型。 記憶體緩衝 磁碟緩衝 ​ 記憶體緩衝是將數據緩衝在記憶體中,供應用程式直接讀取和使用。優點是讀取速度快。缺點是由於記憶體資源有限, ...
  • gnvm 是一個簡單的 Windows 下 Node.js 多版本管理工具,它可以實現安裝管理多個node版本,同時只需要一條命令就可以完成node版本之間的切換和版本更新等操作;寫Vue框架項目時非常的方便,可以解決新舊項目之間因node版本引起的bug等問題。(類似的還有工具 nvm nvmw ... ...
  • 摘要:先彙總相關股票價格,然後有選擇地對其分類,再計算移動均線、布林線等。 一、彙總數據 彙總整個交易周中從周一到周五的所有數據(包括日期、開盤價、最高價、最低價、收盤價,成交量等),由於我們的數據是從2020年8月24日開始導出,數據多達420條,先截取部分時間段的數據,不妨先讀取開始20個交易日 ...
  • EL和JSTL都是JSP的內容的拓展,都是開發的一些東西,稍微學習記錄一下,避免以後忘記 ...
  • 做這些業務設計時,核心思想是:把常用的邏輯進行封裝,流程設計為可配置,這樣即可在一定時間內應對業務的需求和變化,降低開發成本的支出,從而使研發更側重核心業務的管理和抽象封裝等內容。 ...
  • 需求是用java程式獲取txt文件中的數據並將姓名、職稱、工資添加到新txt文件中,txt文件中數據的格式是固定的,如下: 添加後的格式是這樣的: 這裡不考慮工資是怎麼算的,只說獲取數據和寫入數據的方法。 教師姓名和職稱之間是有空格的,而職稱和下一個教師之間是有換行的。 通過查閱資料,我發現了一個特 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...