手撕線程池

線程池的目的:

1.復用線程,減少頻繁創建和銷毀的開銷

  • 創建和銷毀線程是昂貴的系統操作,涉及內核調度、內存分配;

  • 使用線程池預先創建一批線程,在多個任務間循環復用,避免資源浪費,提高性能

2.主線程不阻塞,讓異步線程處理任務

  • 主線程可以專注于接收任務或調度邏輯;

  • 實際的耗時操作(如 IO、計算)由線程池中的工作線程執行;

  • 這樣可以提升程序響應速度、增強并發能力

3.控制線程數量,避免線程過多導致資源競爭

線程池原理:

任務隊列+一組工作線程+條件變量調度機制(喚醒阻塞的工作線程處理任務)

1.任務隊列 1.提供push放入任務的接口 2.pop獲取任務的接口(沒有 工作線程會阻塞) ????????3.cancel析構線程池?通過mutex保證同步+condition_variable控制工作線程

2.調度器 1.初始化創建工作線程 2.提供工作線程的入口函數work()從隊列中循環pop獲取任務處理 沒有任務會阻塞的pop函數中。3.提供Post 主線程向隊列中放入任務的接口。

單生產者對多消費者(單隊列)

1.調度器ThreadPool

1.構造函數

創建一定數量的工作線程?并進行管理。

2.Woker() 工作線程的入口函數

循環處理隊列中的任務,通過Pop()獲取任務 輸出型參數task帶回任務,沒有任務就回阻塞在Pop()函數中。

3.Post() 向隊列中放入任務的接口

bind綁定函數 將閉包對象存入任務隊列中

4.析構函數

對每個退出的線程進行join()等待回收

2.任務隊列

1.Push()入隊列接口

先加鎖 把任務放入到隊列中 解鎖后再用條件變量喚醒一個工作線程,進行處理。

2.Pop() 出隊列

輸出型參數value 返回給上層任務。條件變量阻塞線程,等有隊列有任務才會解除阻塞 或者退出的時候。

3.Cancel()?

線程池析構邏輯,設置為非阻塞_nonblock=true,并喚醒所有線程 進行退出。

此時在Pop()被阻塞的線程被喚醒 返回false,工作線程break退出循環。

多生產者對多消費者(雙隊列)

上面單隊列的適合一個生產者對應多個消費者的場景,如果多對多 那鎖的沖突就大了。對此我們采用的是雙緩沖區的策略,即生產者和消費者各一個隊列。

工作線程消費完工作隊列中的數據后 會進行交換隊列的操作,繼續進行處理。

1.調度器

不改變也沒問題,但對于這個向隊列中放入任務的Post()函數 原本只能存入std::function<void()>類型的函數。為了解決函數有返回值,上層獲取到返回值,我們這樣設置Post函數。

這是一個線程池任務提交函數(Post),它接受任意可調用對象 f 和參數 args...,并返回一個 std::future,用于異步獲取結果。

1.實現要解決 傳入的函數F的返回值類型是什么。可以用類型萃取工具std::invoke_result_t<>獲取F函數 參數Args...的返回值類型。但參數的類型等模板推導完才行,因此采用了

auto post()->類型 尾置返回值類型的語法(告訴編譯器等模板推導完再 告訴你返回值的類型)

2.現在知道了返回值類型,怎么把返回值給上層?

1.promise+futrue 在任務函數中手動設置結果。但還要更好的選擇

2.packaged_task<>+future異步任務封裝器,對函數進行封裝 返回閉包對象。讓線程執行這個閉包對象 等函數執行完自動把結果進行設置,就不用手動設置結果了。

1.template <typename F, typename... Args>????????

? ?F代表函數類型? ...Args函數的參數? ? ?

2.std::invoke_result_t<F, Args...>類型萃取工具

????用來獲取調用某個函數對象 F 以參數 Args... 所產生的返回類型。讓編譯器自己推導函數的返回類型?

3.auto Post(...) -> std::future<std::invoke_result_t<F, Args...>>尾置返回類型語法

?它是函數返回類型的一種寫法,主要用于模板函數中返回類型依賴參數類型的情況

? ? ? ? 1.auto讓編譯器先跳過函數的返回類型 先去處理函數的參數

? ? ? ? 2.推導出函數參數F Args...的類型

? ? ? ? 3.推導完函數參數的類型 才能推導出函數的返回值類型

為什么不能寫成這樣?

std::future<std::invoke_result_t<F, Args...>> Post(F&& f, Args&&... args);

因為編譯器解析函數聲明時是從左到右的,獲取返回值的類型std::future<std::invoke_result_t<F, Args...>> 但F Args..的類型必須等參數傳入后,才能確認。

編譯器處理函數模板時是按“返回值 → 函數名 → 參數”的順序進行的,你不能在返回值里使用還沒推導出的模板參數類型,這就是為什么需要尾置返回類型的根本原因。

4.std::packaged_task<>異步任務封裝器

std::packaged_task 是一個 把函數和返回值綁定起來的包裝器可以讓函數的執行結果通過 std::future 異步獲取。

它是 C++ 標準庫中為了解決“函數執行完以后怎么拿返回值”的問題而設計的組件。

eg.使用方法

int add(int a, int b) { return a + b; }
//1.用 packaged_task+bind 包裝它函數
std::packaged_task<int()> task(std::bind(add, 2, 3));
//2.獲取關聯的future
std::future<int> result = task.get_future();
//3.異步執行這個任務(線程、線程池、手動調用都可以)
std::thread t(std::move(task));  // 線程里執行 task()
t.join();
//4.fet()獲取返回值
std::cout << result.get();

和promise最大的區別是,不需要手動設置set_value的值,等bind綁定的函數執行完 自動把函數的返回值填入,之后get()就可以獲取到函數的返回值。

對比點std::packaged_taskstd::promise
是否自動設置 future 的值?? 是的,執行函數后自動設置? 需要你手動調用 .set_value()
誰負責填充結果?函數執行完后自動填充你手動調用 .set_value()
是否必須綁定函數?? 是的? 否,傳的是值

packaged_task 最適合線程池,因為線程池就是“執行任務然后獲得返回值”,它天然對接任務隊列。

“執行一個函數并得到它的返回值” —— 用 packaged_task;

promise 更多用于線程間通信,或者異步控制信號(比如網絡回調、I/O完成通知等)。

“告訴另一個線程一個值” —— 用 promise。

2.任務隊列(雙)

和單隊列不同的地方是,Pop()工作線程獲取任務時 如果沒有任務,不會阻塞在Pop()而是會進行Swap()交換隊列(生產者隊列有數據或者非阻塞)

? ? ? ? if(_con_queue.empty()&&SwapQueue()==0)

先判斷消費者隊列是否為空,不為空就不會繼續后面的判斷了,也就不會進行交互隊列。

只有消費者隊列為空才會進行交換。SwapQueue()會返回交換后的隊列有多少任務 ==0說明隊列為空(但生產者隊列有數據才會交換 沒有數據就會阻塞,除非要退出 會設置為非阻塞)

,所以交換完隊列還為空就需要返回false進行退出。

交換時 處理消費者需要加鎖,生產者也需要加鎖。

等生產者隊列有數據再進行交換,或者退出設為非阻塞

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/911664.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/911664.shtml
英文地址,請注明出處:http://en.pswp.cn/news/911664.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

3DTiles三維模型

1. 3DTiles 介紹? 2016 年&#xff0c;Cesium 團隊借鑒傳統 2DGIS 的地圖規范&#xff1a;WMTS&#xff0c;借鑒圖形學中的層次細節模型&#xff0c;打造出大規模的三維數據標準&#xff1a;3d-Tiles&#xff0c;中文譯名&#xff1a;三維瓦片。 它在模型上利用了 gltf 渲染…

Golang Kratos 系列:業務分層的若干思考(一)

在使用 Kratos 框架開發云服務的過程中&#xff0c;漸漸理解和感受到“領域層”這個概念和抽象的強大之處&#xff0c;它可以將業務和存儲細節解耦、將業務和開發初期頻繁變更的API結構&#xff0c;讓Mock單元測試變得更加容易、對細節的變化更魯棒。讓業務代碼擺脫技術細節依賴…

深度優化OSS上傳性能:多線程分片上傳 vs 斷點續傳實戰對比

1 卸載開頭 對象存儲服務&#xff08;OSS&#xff09;已成為現代應用架構的核心組件&#xff0c;但隨著業務規模擴大&#xff0c;文件上傳性能問題日益凸顯。本文將深入探討兩種核心優化技術&#xff1a;多線程分片上傳和斷點續傳&#xff0c;通過理論分析、代碼實現和性能測試…

doris_工作使用整理

文章目錄 前言一、doris整體情況二、doris的存儲過程情況1.分類2. 同步物化視圖3. 異步物化視圖三,分區相關1.分區建的過多前言 提示:doris使用版本3.x 提示:以下是本篇文章正文內容,下面案例可供參考 一、doris整體情況 細節放大 二、doris的存儲過程情況 1.分類 按…

左神算法之單輔助棧排序算法

目錄 1. 題目2. 解釋3. 思路4. 代碼5. 總結 1. 題目 請編寫一個程序&#xff0c;對一個棧里的整型數據&#xff0c;按升序進行排序&#xff08;即排序前棧里的數據是無序的&#xff0c;排序后最大元素位于棧頂&#xff09;。要求最多只能使用一個額外的棧存放臨時數據&#xf…

使用Trae編輯器與MCP協議構建高德地圖定制化服務

目錄 一、使用Trae編輯器配置高德MCP Server 1.1 Trae介紹 1.2 從mcp.so中獲取配置高德地圖mcp server配置信息 1.3 高德地圖開發者配置 1.4 添加Filesystem 到Trae 1.5 使用結果展示 1.6 MCP常見命令行工具和包管理說明 1.7 Function Call工具和MCP技術對比 二、本地…

【LLaMA-Factory 實戰系列】三、命令行篇 - YAML 配置與高效微調 Qwen2.5-VL

【LLaMA-Factory 實戰系列】三、命令行篇 - YAML 配置與高效微調 Qwen2.5-VL 1. 引言2. 為什么從 WebUI 轉向命令行&#xff1f;3. 準備工作&#xff08;回顧&#xff09;4. 核心&#xff1a;創建并理解訓練配置文件4.1 選擇并復制基礎模板4.2 逐一解析與修改配置文件4.3 參數詳…

推薦:ToB銷售B2B銷售大客戶營銷大客戶銷售培訓師培訓講師唐興通講銷售技巧數字化銷售銷AI銷售如何有效獲取客戶與業績

站在AI浪潮之巔&#xff0c;重塑銷售之魂 在AI時代&#xff0c;普通銷售人員&#xff08;TOB、TOC&#xff09;除了傳統的銷售動作之外&#xff0c;還能做什么&#xff1f;怎么做&#xff1f; 這是《AI銷冠》這本書想探討的核心問題。 特別喜歡編輯老師總結的&#xff1a; 讀者…

爬取小紅書相關數據導入到excel

本期我們來進行實戰,爬取小紅書的相關數據導入到excel中,后續可進行些數據分析,今后或者已經在運營小紅書的小伙伴應該比較喜歡這些數據。今天我們的主角是DrissionPage,相對于之前介紹的selenium省去了很多的配置,直接安裝了就能使用。 DrissionPage 是一個基于 python …

c++面試題每日一學記錄- C++對象模型與內存對齊深度原理詳解

一、C++對象模型核心原理 1. 對象內存布局基礎原理 設計哲學: 零開銷原則:不為未使用的特性付出代價(如無虛函數則無vptr)兼容性:C結構體在C++中保持相同內存布局多態支持:通過虛函數表實現運行時動態綁定內存布局實現機制: 編譯器處理步驟: 成員排列:嚴格按聲明順序…

Kafka 監控與調優實戰指南(二)

五、Kafka 性能問題剖析 5.1 消息丟失 消息丟失是 Kafka 使用過程中較為嚴重的問題&#xff0c;可能由多種原因導致。在生產者端&#xff0c;如果配置不當&#xff0c;比如將acks參數設置為0&#xff0c;生產者發送消息后不會等待 Kafka broker 的確認&#xff0c;就繼續發送…

Linux下SVN報錯:Unable to connect to a repository at URL ‘svn://XXX‘

一、問題描述 Linux下通過SVN執行提交&#xff08;commit&#xff09;操作時報錯&#xff1a;Unable to connect to a repository at URL svn://XXX&#xff1a; 二、解決方法 導致該問題的一個可能原因是遠程倉庫的URL發生變化了&#xff0c;即svn服務器的ip變更了。這時可…

Modbus 掃描 從站號、波特率

下載鏈接&#xff1a;https://pan.quark.cn/s/533ceb8e397d 下載鏈接: https://pan.baidu.com/s/1PQHn-MwfzrWgF2UrXQDoGg 提取碼: 1111

Docker 容器通信與數據持久化

目錄 簡介 一、Docker 容器通信 1. Docker 網絡模式 2. Bridge 模式 3. Host 模式 4. Container 模式 5. Overlay 模式 6. 端口映射&#xff1a;容器與外部的橋梁 7. 容器互聯&#xff1a;從 --link 到自定義網絡 二、Docker 數據持久化 1. 數據卷&#xff1a;Docke…

【教學類-89-08】20250624新年篇05——元宵節燈籠2CM黏貼邊(倒置和正立數字 )

背景需求&#xff1a; 【教學類-89-06】20250220新年篇05——元宵節燈籠2CM黏貼邊&#xff08;3邊形到50邊形&#xff0c;一頁1圖、2圖、4圖&#xff0c;適合不同水平&#xff0c;適合不同階段&#xff09;-CSDN博客文章瀏覽閱讀1.6k次&#xff0c;點贊35次&#xff0c;收藏27…

【DB2】SQL0104N An unexpected token “OCTETS“ was found following “……

db2創建表時報標題的錯誤&#xff0c;建表語句如下 db2 "CREATE TABLE YS.TEST_1(ID VARCHAR(64 OCTETS))"去掉octets就好了 經過測試&#xff0c;在9.7版本報錯&#xff0c;在10.5.11沒問題&#xff0c;懷疑版本差異導致 在官網查找資料&#xff0c;應該是10.5才…

暴雨以信創委員會成員單位身份參與南京專題活動

6月19日&#xff0c;中國電子工業標準化技術協會信息技術應用創新工作委員會&#xff08;簡稱信創工委會&#xff09;聯合南京市工業和信息化局共同舉辦的“智啟未來&#xff1a;AI賦能信息技術應用創新辦公新勢力”專題活動在南京成功舉辦。南京市工業和信息化局副局長代吉上、…

基于keepalived、vip實現高可用nginx (centos)

基于keepalived、vip實現高可用nginx &#xff08;centos&#xff09; 1、安裝keepalived yum install keepalived2、選同一局域網空置ip作vip 我這里測試是&#xff1a; 主&#xff1a;192.168.163.134 副&#xff1a;192.168.163.135 vip&#xff1a;192.168.163.1403、ke…

使用 launch 啟動 rviz2 并加載機器人模型

視頻資料&#xff1a;《ROS 2機器人開發從入門到實踐》6.2.2 在RViz中顯示機器人_嗶哩嗶哩_bilibili 1、創建工作空間 chapt6_ws/src&#xff0c;創建包 fishrobot_description ros2 create fishrobot_description --build-type ament_cmake --license Apache-2.0 2、創建機器…

華為云Flexus+DeepSeek征文 | 基于CCE容器的AI Agent高可用部署架構與彈性擴容實踐

華為云FlexusDeepSeek征文 | 基于CCE容器的AI Agent高可用部署架構與彈性擴容實踐 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 總有一行代碼&#xff0c;能點亮萬千星辰。 &#x1f50d; 在技術的宇宙中&#xff0c;我愿做永不停歇的探索者。 …