線程池實現學習筆記1

線程池實現學習筆記

今天花了一些時間學習和實現了線程池,收獲頗豐。在這里記錄一下自己的學習心得,希望對大家也有幫助。

為什么需要線程池?

在實際開發中,如果每個任務都創建一個新線程,當任務數量很大時會帶來以下問題:

  1. 線程創建和銷毀的開銷大

    • 創建線程需要分配內存空間
    • 需要初始化線程棧(默認大小通常為1MB)
    • 需要進行系統調用創建內核線程
    • 銷毀線程需要回收資源,也會消耗系統資源
  2. 系統資源占用過多

    • 每個線程都占用一定的內存空間
    • 線程過多會導致系統內存緊張
    • 線程的上下文信息也會占用內核空間
  3. 線程數量過多導致系統不穩定

    • 超出系統支持的最大線程數限制
    • 過多的線程競爭CPU資源
    • 可能導致系統響應變慢
  4. 線程頻繁切換導致CPU開銷增大

    • 線程切換需要保存和恢復上下文
    • 頻繁的上下文切換會降低CPU利用率
    • 影響程序的整體性能

這時候就需要線程池來解決這些問題。線程池通過復用已創建的線程來執行任務,避免了頻繁創建和銷毀線程的開銷。

線程池的核心數據結構

首先,我們需要定義線程池的基本結構。每個結構體的設計都有其特定的用途:

typedef struct threadpool_t {pthread_t *thread;         // 線程數組,存儲線程池中的線程int threadd_nums;         // 線程數量,表示池中的線程數task_queue_t que;        // 任務隊列,存儲待執行的任務pthread_mutex_t mutex;    // 互斥鎖,用于保護共享資源pthread_cond_t cond;     // 條件變量,用于線程間的通知機制bool is_running;         // 線程池運行狀態,控制線程池的生命周期
} threadpool_t;// 任務結構體:定義任務的基本單位
typedef struct task_t {void (*function)(void* arg);  // 函數指針,指向任務函數void* arg;                    // 函數參數,可以傳遞任意類型的數據
} task_t;// 任務隊列結構體:實現任務的FIFO管理
typedef struct task_queue_t {task_t* tasks;           // 任務數組,存儲隊列中的任務int capacity;           // 隊列容量,表示最大可以存儲的任務數int size;              // 當前任務數量,表示隊列中實際的任務數int front;             // 隊首索引,指向第一個任務int rear;              // 隊尾索引,指向下一個可以插入任務的位置
} task_queue_t;

詳細實現過程

1. 任務隊列的實現

任務隊列采用循環隊列的設計,這樣可以高效地利用空間:

// 初始化隊列:分配內存并初始化各項參數
void queueInit(task_queue_t* queue) {queue->capacity = QUEUE_SIZE;    // 設置隊列容量queue->size = 0;                 // 初始化任務數量為0queue->front = 0;                // 隊首指向0queue->rear = 0;                 // 隊尾指向0// 分配任務數組內存queue->tasks = (task_t*)malloc(sizeof(task_t) * QUEUE_SIZE);
}// 添加任務:將任務添加到隊列尾部
bool queuePush(task_queue_t* queue, task_t task) {// 檢查隊列是否已滿if (queue->size >= queue->capacity) {return false;}// 將任務添加到隊尾queue->tasks[queue->rear] = task;// 更新隊尾位置,使用取模運算實現循環queue->rear = (queue->rear + 1) % queue->capacity;queue->size++;return true;
}// 獲取任務:從隊列頭部取出任務
bool queuePop(task_queue_t* queue, task_t* task) {// 檢查隊列是否為空if (queue->size <= 0) {return false;}// 取出隊首任務*task = queue->tasks[queue->front];// 更新隊首位置,使用取模運算實現循環queue->front = (queue->front + 1) % queue->capacity;queue->size--;return true;
}

2. 工作線程函數實現

工作線程的實現體現了線程池的核心工作原理:

void* threadFunc(void* arg) {threadpool_t* pool = (threadpool_t*)arg;task_t task;while (pool->is_running) {  // 線程池運行狀態檢查pthread_mutex_lock(&pool->mutex);  // 獲取互斥鎖// 使用while循環是為了防止虛假喚醒while (pool->que.size == 0 && pool->is_running) {// 當隊列為空時,線程等待條件變量通知pthread_cond_wait(&pool->cond, &pool->mutex);}// 再次檢查運行狀態,防止線程池已關閉if (!pool->is_running) {pthread_mutex_unlock(&pool->mutex);break;}// 嘗試獲取任務if (queuePop(&pool->que, &task)) {pthread_mutex_unlock(&pool->mutex);  // 解鎖,讓其他線程能夠訪問隊列task.function(task.arg);  // 執行任務} else {pthread_mutex_unlock(&pool->mutex);}}return NULL;
}

3. 線程池的完整生命周期

初始化
void threadpoolInit(threadpool_t *pool, int num) {if (pool) {// 分配線程數組內存,使用calloc自動初始化為0pool->thread = (pthread_t *)calloc(num, sizeof(pthread_t *));pool->threadd_nums = num;  // 設置線程數量pool->is_running = true;   // 設置運行狀態為true// 初始化同步原語pthread_mutex_init(&pool->mutex, NULL);  // 初始化互斥鎖pthread_cond_init(&pool->cond, NULL);    // 初始化條件變量queueInit(&pool->que);                   // 初始化任務隊列}
}
啟動線程池
void threadpoolStart(threadpool_t *pool) {// 創建指定數量的工作線程for (int i = 0; i < pool->threadd_nums; i++) {// 創建線程,并將線程池指針作為參數傳遞int ret = pthread_create(&pool->thread[i], NULL, threadFunc, pool);if (ret != 0) {// 錯誤處理:打印錯誤信息并退出fprintf(stderr, "pthread_create failed: %s\n", strerror(ret));exit(1);}}
}
添加任務
void threadpoolAddTask(threadpool_t* pool, task_t task) {pthread_mutex_lock(&pool->mutex);  // 獲取互斥鎖// 將任務添加到隊列if (queuePush(&pool->que, task)) {// 成功添加任務后,喚醒一個等待的線程pthread_cond_signal(&pool->cond);}pthread_mutex_unlock(&pool->mutex);  // 釋放互斥鎖
}
銷毀線程池
void threadpoolDestroy(threadpool_t *pool) {if (pool) {// 設置停止標志,通知所有線程準備退出pool->is_running = false;// 喚醒所有等待的線程,使其檢查運行狀態并退出pthread_cond_broadcast(&pool->cond);// 等待所有線程完成當前任務并退出for (int i = 0; i < pool->threadd_nums; i++) {pthread_join(pool->thread[i], NULL);}// 按順序釋放所有資源free(pool->thread);              // 釋放線程數組queueDestroy(&pool->que);        // 釋放任務隊列pthread_mutex_destroy(&pool->mutex);  // 銷毀互斥鎖pthread_cond_destroy(&pool->cond);    // 銷毀條件變量}
}

實際使用示例

下面是一個完整的使用示例,展示了線程池的基本用法:

// 任務函數:打印任務編號并模擬耗時操作
void taskFunc(void* arg) {int num = *(int*)arg;printf("Task %d is running\n", num);sleep(1);  // 模擬任務執行時間free(arg); // 釋放參數內存
}int main() {threadpool_t pool;// 初始化線程池,創建4個工作線程threadpoolInit(&pool, 4);threadpoolStart(&pool);// 添加10個任務到線程池for (int i = 0; i < 10; i++) {// 為每個任務分配參數內存int* arg = malloc(sizeof(int));*arg = i;// 創建任務并添加到線程池task_t task = {taskFunc, arg};threadpoolAddTask(&pool, task);}sleep(5);  // 等待任務執行完成threadpoolDestroy(&pool);  // 銷毀線程池return 0;
}

遇到的問題和解決方案

在實現過程中,我遇到了一些典型的并發編程問題:

  1. 內存管理問題

    • 任務參數的內存泄漏:
      • 問題:任務參數是動態分配的,如果不及時釋放會造成內存泄漏
      • 解決:在任務函數執行完成后釋放參數內存
    • 線程池資源的釋放順序:
      • 問題:資源釋放順序不當可能導致訪問已釋放的內存
      • 解決:先停止所有線程,再按照依賴關系順序釋放資源
  2. 線程安全問題

    • 任務隊列的并發訪問:
      • 問題:多個線程同時操作隊列可能導致數據競爭
      • 解決:使用互斥鎖保護隊列的所有操作
    • 條件變量的使用:
      • 問題:可能發生虛假喚醒,導致線程在隊列為空時仍被喚醒
      • 解決:使用while循環檢查條件,配合互斥鎖使用
  3. 死鎖問題

    • 在獲取任務時可能發生死鎖:
      • 問題:鎖的獲取和釋放順序不當
      • 解決:保證所有線程按相同順序獲取鎖,及時釋放鎖
    • 銷毀時的死鎖:
      • 問題:線程池銷毀時,線程可能還在等待條件變量
      • 解決:先設置停止標志,再喚醒所有等待的線程

性能優化

  1. 任務隊列優化

    • 使用無鎖隊列提高并發性能:
      • CAS操作替代互斥鎖
      • 使用內存屏障保證內存順序
    • 實現動態擴容的任務隊列:
      • 監控隊列使用率
      • 適時調整隊列容量
  2. 線程管理優化

    • 實現動態線程數量調整:
      • 根據任務量動態增減線程
      • 設置合理的線程數閾值
    • 添加線程池負載監控:
      • 記錄任務執行時間
      • 統計線程利用率

后續優化方向

  1. 添加線程池狀態管理

    • 運行狀態監控:
      • 記錄線程池運行狀態
      • 提供狀態查詢接口
    • 任務執行統計:
      • 統計任務執行次數和時間
      • 生成性能報告
  2. 實現動態擴縮容

    • 基于任務量自動調整線程數:
      • 設置任務隊列水位線
      • 根據水位線調整線程數
    • 設置最大最小線程數限制:
      • 防止線程數過多或過少
      • 保證系統穩定性
  3. 添加任務優先級支持

    • 實現優先級隊列:
      • 多級任務隊列
      • 優先級調度算法
    • 支持任務取消和超時:
      • 任務狀態管理
      • 超時處理機制
  4. 完善錯誤處理機制

    • 異常處理和恢復:
      • 捕獲并處理異常
      • 實現自動恢復機制
    • 日志記錄系統:
      • 記錄關鍵操作日志
      • 支持不同級別的日志

這個實現雖然還有優化空間,但已經包含了線程池的核心功能。在實際項目中,我們可以根據具體需求進行擴展和改進。

希望這篇文章能幫助到同樣在學習線程池的朋友們。如果有任何問題或建議,歡迎在評論區討論!

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

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

相關文章

CES Asia 2025賽逸展:科技浪潮中的創新與商貿盛會

在科技發展日新月異的當下&#xff0c;CES Asia 2025第七屆亞洲消費電子技術貿易展&#xff08;賽逸展&#xff09;正積極籌備&#xff0c;將在北京舉辦&#xff0c;有望成為亞洲消費電子領域極具影響力的年度盛會。作為亞洲科技領域的重要展會&#xff0c;此次得到了數十家電子…

架構設計之自定義延遲雙刪緩存注解(上)

架構設計之自定義延遲雙刪緩存注解(上) 小薛博客官方架構設計之自定義延遲雙刪緩存注解(上)地址 1、業務場景問題 在多線程并發情況下&#xff0c;假設有兩個數據庫修改請求&#xff0c;為保證數據庫與redis的數據一致性&#xff0c;修改請求的實現中需要修改數據庫后&#…

Windows桌面采集技術

在進入具體的方式討論前&#xff0c;我們先看看 Windows 桌面圖形界面的簡化架構&#xff0c;如下圖&#xff1a; 在 Windows Vista 之前&#xff0c;Windows 界面的復合畫面經由 Graphics Device Interface&#xff08;以下簡稱 GDI&#xff09;技術直接渲染到桌面上。 在 Wi…

ElementPlus 快速入門

目錄 前言 為什么要學習 ElementPlus&#xff1f; 正文 步驟 1 創建 一個工程化的vue 項目 ?2 安裝 element-Plus :Form 表單 | Element Plus 1 點擊 當前界面的指南 2 點擊左邊菜單欄上的安裝&#xff0c;選擇包管理器 3 運行該命令 demo(案例1 &#xff09; 步驟 …

TypeScript語言的設備管理

TypeScript 設備管理系統的設計與實現 引言 在現代社會&#xff0c;設備管理已成為企業和組織運營中不可或缺的一部分。無論是IT設備、辦公家具還是生產機器&#xff0c;企業都需要一種有效的方式來管理、追蹤和維護這些資產。隨著前端技術的不斷發展&#xff0c;TypeScript作…

Ubuntu20.04.6系統根目錄擴容

文章目錄 方法一&#xff1a;**1. 檢查磁盤和分區情況****2. 擴展 vda3 分區****3. 擴展 LVM 物理卷****4. 擴展 LVM 邏輯卷****5. 擴展文件系統** 方法二:1. 查看當前磁盤分區情況2. 創建新分區3. 重新加載分區表4. 擴展物理卷&#xff08;PV&#xff09;5. 擴展邏輯卷&#x…

[藍橋杯 2023 省 A] 異或和之和

題目來自洛谷網站&#xff1a; 暴力思路&#xff1a; 先進性預處理&#xff0c;找到每個點位置的前綴異或和&#xff0c;在枚舉區間。 暴力代碼&#xff1a; #include<bits/stdc.h> #define int long long using namespace std; const int N 1e520;int n; int arr[N…

python學習筆記--實現簡單的爬蟲(二)

任務&#xff1a;爬取B站上最愛歡迎的編程課程 網址&#xff1a;編程-嗶哩嗶哩_bilibili 打開網頁的代碼模塊&#xff0c;如下圖&#xff1a; 標題均位于class_"bili-video-card__info--tit"的h3標簽中&#xff0c;下面通過代碼來實現&#xff0c;需要說明的是URL中…

微服務分層架構詳解:表示層、應用層與基礎設施層的協同工作

微服務分層架構詳解&#xff1a;表示層、應用層與基礎設施層的協同工作 文章目錄 微服務分層架構詳解&#xff1a;表示層、應用層與基礎設施層的協同工作1. 表示層&#xff08;Presentation Layer&#xff09;1.1 表示層的作用1.2 技術選型1.3 表示層的挑戰 2. 應用層&#xff…

[C++面試] 你了解transform嗎?

層級核心知識點入門基本語法、與for_each對比、單/雙范圍操作進階動態擴展、原地轉換、類型兼容性、異常安全高階性能優化、C20 Ranges、transform_if模擬 一、入門 1、描述std::transform的基本功能&#xff0c;并寫出兩種版本的函數原型 std::transform函數是 C 標準庫<…

windows清除電腦開機密碼,可保留原本的系統和資料,不重裝系統

前言 很久的一臺電腦沒有使用了&#xff0c;開機密碼忘了&#xff0c;進不去系統 方法 1.將一個閑置u盤設置成pe盤&#xff08;注意&#xff0c;這個操作會清空原來u盤的數據&#xff0c;需要在配置前將重要數據轉移走&#xff0c;數據無價&#xff0c;別因為配置這個丟了重…

5.4 位運算專題:LeetCode 137. 只出現一次的數字 II

1. 題目鏈接 LeetCode 137. 只出現一次的數字 II 2. 題目描述 給定一個整數數組 nums&#xff0c;其中每個元素均出現 三次&#xff0c;除了一個元素只出現 一次。請找出這個只出現一次的元素。 要求&#xff1a; 時間復雜度為 O(n)&#xff0c;空間復雜度為 O(1)。 示例&a…

C語言:掃雷

在編程的世界里&#xff0c;掃雷游戲是一個經典的實踐項目。它不僅能幫助我們鞏固編程知識&#xff0c;還能鍛煉邏輯思維和解決問題的能力。今天&#xff0c;就讓我們一起用 C 語言來實現這個有趣的游戲&#xff0c;并且通過圖文并茂的方式&#xff0c;讓每一步都清晰易懂 1. 游…

【論文#目標檢測】YOLO9000: Better, Faster, Stronger

目錄 摘要1.引言2.更好&#xff08;Better&#xff09;3.更快&#xff08;Faster&#xff09;4.更健壯&#xff08;Stronger&#xff09;使用 WordTree 組合數據集聯合分類和檢測評估 YOLO9000 5.結論 Author: Joseph Redmon; Ali Farhadi Published in: 2017 IEEE Conference …

數據庫誤更新操作 如何回滾

1.未提交 直接 rollback 2.已提交 步驟 查詢指定時間內修改前數據庫數據&#xff1a; -- 查詢誤操作前的數據&#xff08;例如 10 分鐘前&#xff09; SELECT * FROM 表名 AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL 10 MINUTE) WHERE 條件;-- 將數據恢復&#xff08;需確保有…

大數據運維實戰之YARN任務內存泄露排查實戰:從節點掉線到精準定位的完整指南

1.問題背景&#xff1a;集群內存風暴引發的危機 最近某大數據集群頻繁出現節點掉線事故&#xff0c;物理內存監控持續爆紅。運維人員發現當節點內存使用率達到95%以上時&#xff0c;機器會進入不可響應狀態&#xff0c;最終導致服務中斷。這種"內存雪崩"現象往往由單…

AI+金融 應用 使用DeepSeek、Qwen等大模型輸入自然語言,得到通達信等行情軟件公式代碼,導入后使用

AI金融 應用 使用DeepSeek、Qwen等大模型輸入自然語言&#xff0c;得到通達信等行情軟件公式代碼&#xff0c;導入后使用。不會編程&#xff0c;也能行情軟件中實現個性化條件選股&#xff0c;個性化技術指標。 AIbxm低估值趨勢選股策略&#xff0c;參考提示詞&#xff1a; 編…

[Xilinx]工具篇_PetaLinux自動編譯

[Xilinx]工具篇_PetaLinux自動編譯 若該文為原創文章&#xff0c;未經允許不得轉載風釋雪QQ:627833006E-mail:hn.cyfoxmail.comCSDN博客: https://blog.csdn.net/weixin_46718879知乎&#xff1a;https://www.zhihu.com/people/abner-80-4 1.版本 日期作者版本說明2025XXXX風釋…

多語言語料庫萬卷·絲路2.0開源,數據模態全面升級,搭建文化交流互鑒AI橋梁

3月22日&#xff0c;上海人工智能實驗室&#xff08;上海AI實驗室&#xff09;聯合新華社新聞信息中心、上海外國語大學、外研在線等&#xff0c;發布全新升級的“萬卷絲路2.0”多語言語料庫&#xff0c;通過構建多語言開源數據底座&#xff0c;以人工智能賦能“一帶一路”高質…

多語言生成語言模型的少樣本學習

摘要 大規模生成語言模型&#xff0c;如GPT-3&#xff0c;是極具競爭力的少樣本學習模型。盡管這些模型能夠共同表示多種語言&#xff0c;但其訓練數據以英語為主&#xff0c;這可能限制了它們的跨語言泛化能力。在本研究中&#xff0c;我們在一個涵蓋多種語言的語料庫上訓練了…