定位和分析解決std::thread創建失敗的問題和解決方法(mmap虛擬地址耗盡)

文章目錄

    • 引言
    • 問題描述和分析
      • 監控shell腳本
      • shell腳本解釋
    • 問題根源追溯
    • 解決方案一:增大mmap區域
    • 解決方案二:優化線程棧空間
    • 解決方案三:引入線程池
    • 參考文章

引言

在高并發和長周期運行的環境中,頻繁創建std::thread線程可能導致mmap虛擬地址空間耗盡,進而引發資源不足的錯誤。
本文提出的增大mmap區域、優化線程棧空間以及引入線程池的策略,能夠有效地管理線程資源,提高應用的穩定性和效率。

問題描述和分析

為處理一些異步任務請求,我們頻繁創建std::thread線程來執行任務。盡管初期運行順利,但隨時間推移,后面會遇到“Resource temporarily unavailable”的異常,直接影響了系統的響應時間和整體穩定性。
為精確診斷,我們設計了一套監控機制,實時捕捉并記錄所有線程狀態變化,同時對core dump進行深入解析,以識別故障線程。分析結果表明,問題源于std::thread線程創建階段,具體表現為EAGAIN錯誤——指示系統資源暫時不可用。

監控shell腳本

iterate_threads_info() {local pids=$(pidof "$1")local output_file="$1.info"# Write header to output file{echo "Record start: $(date)"echo "--------------------------------------------------------------"} >> "$output_file"# Iterate over each process and its threadsfor pid in $pids; doecho "Process $pid" >> "$output_file"cat /proc/$pid/maps >> "$output_file"for tid in /proc/$pid/task/*; dotid=$(basename "$tid"){echo "--------------------------------------------------------------"echo "Thread: $tid"echo "--------------------------------------------------------------"echo "status:"cat /proc/$pid/task/$tid/statusecho "stack:"cat /proc/$pid/task/$tid/stackecho "syscall:"cat /proc/$pid/task/$tid/syscallecho ""} >> "$output_file"doneecho "" >> "$output_file"done
}

shell腳本解釋

  • 參數 $1: 這個參數是函數 iterate_threads_info 的輸入參數,它表示要查詢的進程的名稱。在腳本執行時,你會把要查詢的進程的名稱作為腳本的第一個參數傳遞給這個函數。

    local pids=$(pidof "$1")
    local output_file="$1.info"
    
    • pidof "$1":使用 pidof 命令獲取指定進程名稱(由 $1 提供)對應的進程ID(PID)。這些PID將存儲在 pids 變量中。
    • "$1.info":構造一個輸出文件名,使用傳遞給函數的進程名稱 $1 加上 .info 后綴。這個文件名用于存儲進程及其線程的詳細信息。
  • 輸出文件: output_file 變量用來存儲輸出文件的名稱,在函數執行時會根據傳遞給函數的進程名稱動態生成。

  • 循環遍歷進程和線程:

    • 首先,對于每一個通過 pidof "$1" 獲取的進程ID,腳本會將進程ID打印到輸出文件中,并輸出該進程的 maps 文件內容。
    • 然后,使用 for tid in /proc/$pid/task/* 遍歷該進程的所有線程(/proc/$pid/task/ 下的所有文件和目錄),其中 $pid 是當前進程的PID。
    • 對于每個線程,輸出它的 statusstacksyscall 文件內容到輸出文件中,以及相關的分隔符和空行用于格式化輸出。

問題根源追溯

進一步探究線程創建流程,從C++標準庫std::thread出發,經由POSIX線程API pthread_create,直至內核層面的clonedo_fork函數。核心發現:內核在嘗試分配新線程所需mmap區域時,因虛擬地址空間不足,觸發了EAGAIN錯誤。

解決方案一:增大mmap區域

針對虛擬地址空間不足的問題,我們通過修改內核參數來增大mmap區域。默認情況下,TASK_UNMAPPED_BASE的值為TASK_SIZE / 3,這個值大約是進程虛擬地址空間的1/3,系統通常會將大部分的虛擬地址空間分配給已映射的區域(如代碼段、堆、棧等),只留少量空間給未映射區域。

我們將TASK_UNMAPPED_BASE的值從默認的0x2AAA8000調整至0x10000000。這一調整實際上是將未映射區域的起始地址向高地址移動,擴展了系統的虛擬地址空間中可供動態分配的內存空間。重啟服務后,線程創建成功率大幅提升,系統運行穩定無阻。

解決方案二:優化線程棧空間

除了調整mmap區域外,優化線程棧空間也是提高資源利用率的有效手段。過大的棧空間預分配可能無意間擠占了寶貴的虛擬地址空間。

臨時調整棧空間大小(會話級):

ulimit -s 102400

上述命令可即時將棧空間大小設為100MB,適用于當前會話。

永久調整棧空間大小:
編輯/etc/security/limits.conf,添加如下行:

* soft stack 102400

此設置確保系統長期維持100MB的棧空間大小,防止因分配不當引發的創建失敗。
使用C++接口設置創建的線程棧大小

pthread_attr_t attribute;
pthread_t thread;pthread_attr_init(&attribute);
pthread_attr_setstacksize(&attribute, 10240); // 設置線程棧的大小為10K
pthread_create(&thread,&attribute,foo,0);
pthread_join(thread,0);

解決方案三:引入線程池

為從根本上解決頻繁線程創建帶來的問題,可以采用線程池(Thread Pool)的設計模式。線程池預先創建一組固定數量的工作線程,等待任務到來時再分配給空閑線程執行,而非每次任務都創建新線程。

不僅可以解決mmap虛擬地址空間耗盡的問題,還顯著提高了系統性能和資源利用率,使任務執行更加平滑,避免因線程創建失敗導致的服務中斷,

#ifndef THREAD_POOL_H
#define THREAD_POOL_H#include <mutex>
#include <queue>
#include <thread>
#include <vector>
#include <functional>class ThreadPool {public:explicit ThreadPool(size_t threads);void enqueue(const std::function<void()>& task);~ThreadPool();private:// 線程池配置size_t threads_;// 工作線程std::vector<std::thread> workers_;// 任務隊列和同步std::mutex queue_mutex_;std::queue<std::function<void()>> tasks_;bool stop_;
};// 構造函數創建工作線程
inline ThreadPool::ThreadPool(size_t threads) : threads_(threads), stop_(false) {for (size_t i = 0; i < threads_; ++i) {workers_.emplace_back([this] {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lck(queue_mutex_);// 等待任務或停止信號queue_mutex_.wait(lck, [this] { return this->stop_ || !this->tasks_.empty(); });if (this->stop_ && this->tasks_.empty()) {return;}// 獲取下一個任務task = std::move(this->tasks_.front());this->tasks_.pop();}// 執行任務task();}});}
}// 將任務排隊到線程池中執行
void ThreadPool::enqueue(const std::function<void()>& task) {{std::unique_lock<std::mutex> lck(queue_mutex_);// 停止后不接受任務if (stop_) {throw std::runtime_error("Enqueue on stopped ThreadPool");}// 將任務添加到隊列中tasks_.push(task);}queue_mutex_.notify_one();
}// 析構函數等待工作線程終止
inline ThreadPool::~ThreadPool() {{std::unique_lock<std::mutex> lck(queue_mutex_);stop_ = true;}queue_mutex_.notify_all();for (std::thread& worker : workers_) {worker.join();}
}#endif

參考文章

一個std::thread()線程創建失敗問題分析過程

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

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

相關文章

設計模式8-橋模式

設計模式8-Bridge 橋模式 由來與目的模式定義結構代碼推導1. 類和接口的定義2. 平臺實現3. 業務抽象4. 使用示例總結1. 類數量過多&#xff0c;復雜度高2. 代碼重復3. 不符合單一職責原則4. 缺乏擴展性改進后的設計1. 抽象和實現分離&#xff08;橋接模式&#xff09;2. 抽象類…

學習XDMA—20240709

概覽&#xff1a; 在內部&#xff0c;子系統可以配置為實現多達8個獨立的物理DMA引擎(最多4個H2C和4個C2H)。這些DMA引擎可以映射到單獨的AXI4Stream接口&#xff0c;也可以將共享的AXI4內存映射(MM)接口映射到用戶應用程序。在axis4 MM接口上&#xff0c;PCI Express的DMA/橋接…

智能警衛:Conda包依賴的自動監控之道

智能警衛&#xff1a;Conda包依賴的自動監控之道 引言 在復雜的軟件開發項目中&#xff0c;依賴管理是確保項目健康運行的關鍵環節。Conda作為Python和其他科學計算語言的強大包管理器&#xff0c;提供了依賴監控功能&#xff0c;幫助用戶自動化和簡化依賴項的監控過程。本文…

軟考高級第四版備考--第15天(建設團隊)Develop Team

定義&#xff1a;提高工作能力&#xff0c;促進團隊成員互動&#xff0c;改善團隊整體氛圍以提高項目績效的過程 作用&#xff1a;改進團隊協作、增強人際關系技能、激勵員工、減少摩擦以提升整體項目績效 說明&#xff1a;高效團隊行為&#xff1a; 使用開放與有效的溝通&a…

簡述 JS 中對象的創建和拷貝

在 JavaScript 中&#xff0c;對象是一種非常重要且靈活的數據結構&#xff0c;用于存儲多個值&#xff08;屬性&#xff09;和方法&#xff08;函數&#xff09; 對象的創建和拷貝是日常開發中經常涉及的操作&#xff0c;對于業務邏輯的準確實現有著重要的作用 本文將簡要概…

linux查看目錄下的文件夾命令,find 查找某個目錄,但是不包括這個目錄本身?

linux查看目錄下的文件夾命令&#xff0c;find 查找某個目錄&#xff0c;但是不包括這個目錄本身&#xff1f; Linux中查看目錄下的文件夾的命令是使用ls命令。ls命令用于列出指定目錄中的文件和文件夾。通過不同的選項可以實現顯示詳細信息、按照不同的排序方式以及使用不同的…

Profibus轉ModbusTCP網關模塊實現Profibus_DP向ModbusTCP轉換

Profibus和ModbusTCP是工業控制自動化常用的二種通信協議。Profibus是一種串口通信協議&#xff0c;它提供了迅速靠譜的數據傳輸和各種拓撲結構&#xff0c;如總線和星型構造。Profibus可以和感應器、執行器、PLC等各類設備進行通信。 ModbusTCP是一種基于TCP/IP協議的通信協議…

一次零基礎 自“信息收集“到“權限維持“的滲透測試全程詳細記錄

一、滲透總流程 1.確定目標&#xff1a; 在本靶場中&#xff0c;確定目標就是使用各種掃描工具進行ip掃描&#xff0c;確定目標ip。 2.信息收集&#xff1a; 比如平常挖洞使用fofa&#xff0c;天眼查&#xff0c;ip域名等進行查&#xff0c;在我們這個靶場中比如使用Wappalyz…

基于網絡編碼的 tcp 變種-tcp/nc

tcp/nc 是指 “tcp with network coding”&#xff0c;是一種結合了網絡編碼技術的 tcp 變種&#xff0c;網上資源很少&#xff0c;我也不準備多介紹&#xff0c;只介紹它的核心。 傳統 tcp 在演進過程中一直搞不定效率問題&#xff0c;網絡帶寬在增長&#xff0c;cpu 卻沒有變…

C++類和對象(上篇)

文章目錄 前言一、面向過程和面向對象初步認識 二、類的引入 三、類的定義 六、類的實例化 七、類的對象大小的計算 八、類成員函數的this指針 總結 前言 類和對象是面向對象編程的兩個核心概念。 類是一種抽象的數據類型&#xff0c;是描述對象共同特征和行為的模板。一個類…

yolov5:Conv類參數量計算

Conv是yolov5自定義的類&#xff0c;里邊包含了卷積層、BN層和激活函數 class Conv(nn.Module):# Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)default_act nn.SiLU() # default activationdef __init__(self, c…

點云下采樣有損壓縮

轉自本人博客&#xff1a;點云下采樣有損壓縮 點云下采樣是通過一定規則對原點云數據進行再采樣&#xff0c;減少點云個數&#xff0c;降低點云稀疏程度&#xff0c;減小點云數據大小。 1. 體素下采樣&#xff08;Voxel Down Sample&#xff09; std::shared_ptr<PointClo…

華為機考真題 -- 信道分配

題目描述&#xff1a; 算法工程師小明面對著這樣一個問題&#xff0c;需要將通信用的信道分配給盡量多的用戶&#xff0c; 信道的條件及分配規則如下&#xff1a; 1) 所有信道都有屬性&#xff1a;”階”。階為 r 的信道容量為 2^r 比特&#xff1b; 2) 所有用戶需要傳輸的數…

區間貪心

目錄 1.貪心算法的思想 2.區間貪心算法常用的一些題目類型 1.選擇最多不相交區間問題 P2970 [USACO09DEC] Selfish Grazing S 1.思路分析 2.上代碼 2.區間選點問題 P1250 種樹 1.題目 2.方法一 1.代碼解釋 3.方法二 3.區間合并問題 P2434 [SDOI2005] 區間 1. 思路…

中科海訊 C++初級研發工程師筆試題目

C語言中的const關鍵字有什么作用&#xff1f;為什么要使用const關鍵字&#xff1f; 1 const修飾的變量將會被放到常量區&#xff0c;避免被意外的改動。 const修飾的常量比#define修飾的有更多的優勢&#xff0c;比如可以調試&#xff0c;類型檢查等 2 const修飾的參數可做輸入…

Java集合面試題

Java集合框架 1、List、Set、Map的區別2、ArrayList、LinkedList、Vector區別3、為什么數組索引從0開始&#xff0c;而不是從1開始&#xff1f;4、ArrayList底層的實現原理5、紅黑樹、散列表6、HashMap的底層原理7、HashMap的put方法具體流程8、HashMap的擴容機制9、HashMap是怎…

南方科技大學馬永勝教授給年輕人使用AI工具上的建議

摘要 - 1. AI的未來&#xff0c;是機器人和機器人之間的合作&#xff1b; 2. 行業的發展方向是需求決定的&#xff0c;不要做同質化的發展&#xff0c;要做專/精/特/新&#xff1b; 3. 新質生產力 &#xff08; 科學技術革命性突破 生產要素創新型配置 產業深度轉型升級&…

java通過poi-tl導出word實戰詳細步驟

文章目錄 與其他模版引擎對比1.引入maven依賴包2.新建Word文檔exportWprd.docx模版3.編寫導出word接口代碼4.導出成果 poi-tl是一個基于Apache POI的Word模板引擎&#xff0c;也是一個免費開源的Java類庫&#xff0c;你可以非常方便的加入到你的項目中&#xff0c;并且擁有著讓…

貪心算法-以高校教材管理系統為例

1.貪心算法介紹 1.算法思路 貪心算法的基本思路是從問題的某一個初始解出發一步一步地進行&#xff0c;根據某個優化測度&#xff0c;每一 步都要確保能獲得局部最優解。每一步只考慮一 個數據&#xff0c;其選取應該滿足局部優化的條件。若下 一個數據和部分最優解連在一起…

Pix4Dmapper:無人機測繪的革命性工具

在現代測繪和地理信息系統&#xff08;GIS&#xff09;領域&#xff0c;Pix4Dmapper無疑是一款革命性的工具。作為一名長期使用這款軟件的用戶&#xff0c;我深深感受到它在工作中的重要性和便利性。Pix4Dmapper不僅僅是一款軟件&#xff0c;更是測繪工作者的得力助手&#xff…