首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言高效執行緒池實作:解決驚群效應、跨平台支援與完整生命週期管理

     編輯:關於C語言

C 語言高效執行緒池實作:解決驚群效應、跨平台支援與完整生命週期管理

執行緒池是高併發伺服器開發的基礎設施。本文深入探討如何設計一個避免驚群效應、支援跨平台、具備完整建構/銷毀/任務提交介面的 C 執行緒池,參考 POSIX pthread 標準,並提供完整原始碼與測試驗證。

一、驚群效應與設計動機

驚群:多個執行緒同時在條件變數上等待,單一事件觸發喚醒所有等待執行緒,導致大量上下文切換與資源爭搶,嚴重降低效能。

傳統 pthread_cond_signal 可能喚醒多個執行緒。本設計為每個工作執行緒分配獨立條件變數,任務派發時精確喚醒單一閒置執行緒,徹底消除驚群。

二、核心資料結構

// 任務節點
struct threadjob {
    vdel_f run;                 // 任務函式
    void *arg;                  // 任務參數
    struct threadjob *next;     // 下一任務
};

// 工作執行緒節點(含專屬條件變數)
struct thread {
    pthread_t tid;              // 執行緒 ID
    pthread_cond_t cond;        // 專屬條件變數
    struct thread *next;        // 下一執行緒
};

// 執行緒池管理器
struct threadpool {
    int size;                   // 最大執行緒數
    int curr;                   // 目前總執行緒數
    int idle;                   // 目前閒置執行緒數
    pthread_mutex_t mutex;      // 全域互斥鎖
    struct thread *threads;     // 執行緒鏈表
    struct threadjob *head;     // 任務隊列頭
    struct threadjob *tail;     // 任務隊列尾
};

三、關鍵演算法流程

1. 執行緒建立與註冊 _thread_add

  • 分配 struct thread,初始化條件變數
  • 插入執行緒池鏈表頭部

2. 任務提交 sp_add

  1. 建立任務節點,入隊(尾插法維持 FIFO)
  2. 若有閒置執行緒(idle > 0):
    • 解鎖後 pthread_cond_signal 喚醒第一個執行緒(頭節點)
    • 先解鎖再通知,減少鎖持有時間,提升併發度
  3. 若無閒置但未達上限(curr < size):
    • 建立新執行緒執行 _consumer
    • 註冊新執行緒資訊
  4. 否則任務排隊等待

3. 消費者迴圈 _consumer

static void *_consumer(struct threadpool *pool) {
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    pthread_detach(pthread_self());
    pthread_cleanup_push((vdel_f)pthread_mutex_unlock, &pool->mutex);
    pthread_mutex_lock(&pool->mutex);
    pthread_cond_t *cnd = _thread_get(pool, pthread_self());

    while (1) {
        if (pool->head) {
            // 取出任務
            struct threadjob *job = pool->head;
            pool->head = job->next;
            if (pool->tail == job) pool->tail = NULL;
            pthread_mutex_unlock(&pool->mutex);
            job->run(job->arg);   // 執行任務(無鎖)
            free(job);
            pthread_mutex_lock(&pool->mutex);
            continue;
        }
        // 無任務:進入等待
        pool->idle++;
        int status = pthread_cond_wait(cnd, &pool->mutex);
        pool->idle--;
        if (status == 0) continue;  // 正常喚醒
        // 異常/取消:清理退出
        pool->curr--;
        _thread_del(pool, cnd);
        pthread_mutex_unlock(&pool->mutex);
        pthread_cleanup_pop(0);
        return NULL;
    }
}

4. 執行緒池銷毀 sp_del

  1. 鎖定全域互斥鎖
  2. 遍歷執行緒鏈表:pthread_cancel、銷毀條件變數、釋放節點
  3. 釋放所有任務節點
  4. 解鎖並銷毀互斥鎖
  5. 將池指標置 NULL

四、跨平台支援策略

透過 schead.h 封裝平台差異:

  • 睡眠:Linux usleep(ms*1000) / Windows Sleep(ms)
  • 時間取得:Linux gettimeofday / Windows 自行實作
  • 原子操作:GCC __sync_* 內建函式 / Windows Interlocked* API
  • 執行緒清理:pthread_cleanup_push/pop 兩平台實作不同,統一巨集介面

五、介面設計

// 建立池(預設 128 執行緒)
threadpool_t pool = sp_NEW();           // 巨集簡化

// 提交任務(自動轉型 void*(*)(void*))
sp_ADD(pool, my_task, arg);

// 銷毀池
sp_del(&pool);

六、測試驗證

測試程式提交 10,000 個任務,混合同步/非同步模式,5 秒後銷毀池。Windows/Linux 雙平台編譯通過,輸出顯示任務正確分發執行。

七、原始碼結構

  • schead.h/c:跨平台基礎設施(原子鎖、時間、錯誤處理、工具巨集)
  • scpthread.h/c:執行緒池核心實作
  • test_scpthread.c:壓力測試
  • Makefile:自動化建構

八、效能優化方向

  1. 執行緒查找 _thread_get 目前 O(N) 鏈表遍歷,可改用陣列或雜湊表 O(1)
  2. 大量任務時單一鎖爭用激烈,可採用工作竊取或多隊列架構
  3. 整合 epoll/IOCP 實現非同步 I/O 執行緒池

本實作展示了工業級執行緒池的核心設計思維:精確喚醒避免驚群、RAII 風格資源管理、跨平台抽象層、完整生命週期控制。原始碼可直接整合至專案,或作為進階多執行緒程式設計的教學範本。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved