C++手撕線程池
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>#define LL_ADD(item, list) do{ \item->prev = NULL; \item->next = list; \if (list != NULL) list-> prev = item;\list = item; \
} while(0) #define LL_REMOVE(item, list) do{ \if(item-> prev != NULL) item -> prev -> next = item -> next; \if(item-> next != NULL) item -> next -> prev = item -> prev; \ if(list == item) list = item -> next; \item-> prev = item -> next = NULL; \
} while(0) struct NJOB
{void (*func)(void *arg); // 回調函數每一個任務需要那些參數void *user_data; // 任務執行需要什么參數struct NJOB *prev;struct NJOB *next; //雙向鏈表 隊列
};
// 若干任務組織到一起struct NWORKER
{pthread_t id; // 每一個worker相當于一個線程int terminate;struct NMANAGER *pool;struct NWORKER *prev;struct NWORKER *next;};typedef struct NMANAGER
{pthread_mutex_t mtx; //用于抓取任務pthread_cond_t cond; //當任務隊列為空的時候做的struct NJOB *jobs;struct NWORKER *workers;
} nThreadPool;void *thread_cb(void *arg)
{struct NWORKER *worker = (struct NWORKER*) arg;// 線程從隊列里面拿到一個任務去執行// 線程去爭奪隊列里面的資源// 不斷去執行,是一個循環while(1){// 具體內容是由job的回調函數決定的pthread_mutex_lock(&worker->pool->mtx);while(worker->pool->jobs == NULL){if (worker-> terminate) break;pthread_cond_wait(&worker->pool->cond, &worker->pool-> mtx);}// 如果job為空就讓線程休息// 檢查終止條件if (worker->terminate) {pthread_mutex_unlock(&worker->pool->mtx);break;}struct NJOB *job = worker-> pool-> jobs;if(job != NULL){LL_REMOVE(job, worker-> pool-> jobs); // 取出頭結點}pthread_mutex_unlock(&worker->pool->mtx);// 執行任務if (job != NULL && job -> func != NULL){job-> func(job->user_data);// 注意:這里不釋放job,由任務函數負責釋放user_datafree(job);// 釋放任務結構體本身}}
}// sdkint nThreadPoolCreate(nThreadPool *pool, int numWorkers)
{// if (numWorkers < 1) numWorkers = 1;if (pool == NULL) return -1;memset(pool, 0, sizeof(nThreadPool));//pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;memcpy(&pool->mtx, &blank_mutex, sizeof(pthread_mutex_t));// pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;memcpy(&pool->cond, &blank_cond, sizeof(pthread_cond_t));//int i = 0;for(i =0;i<numWorkers;i++){struct NWORKER *worker = (struct NWORKER *)malloc(sizeof(struct NWORKER));if (worker == NULL){perror("malloc");return -2;}memset(worker, 0 ,sizeof(struct NWORKER) );worker->pool = pool;pthread_create(&worker->id, NULL, thread_cb,worker);LL_ADD(worker, pool->workers);}return 0;
}int nThreadPoolDestroy(nThreadPool *pool)
{if(pool == NULL)return -1;// 設置所有worker的終止標志struct NWORKER *worker = pool-> workers;while (worker != NULL){worker->terminate = 1;worker = worker -> next;}// 廣播條件變量喚醒所有線程pthread_mutex_lock(&pool->mtx);pthread_cond_broadcast(&pool->cond);pthread_mutex_unlock(&pool->mtx);// 等待所有worker線程退出worker = pool->workers;while (worker != NULL) {pthread_join(worker->id, NULL);struct NWORKER *next = worker->next;free(worker);worker = next;}// 銷毀互斥鎖和條件變量pthread_mutex_destroy(&pool-> mtx);pthread_cond_destroy(&pool->cond);// 清空任務隊列(如果有剩余任務)struct NJOB *job = pool->jobs;while (job!= NULL){struct NJOB *next = job->next;free(job);job = next;}// 重置線程池狀態pool-> workers = NULL;pool->jobs = NULL;return 0;}int nThreadPoolPushTask(nThreadPool *pool, struct NJOB *job)
{//如何往線程池丟任務// 往隊列里面加一個節點(job)// 同時通知一個worker(線程)// 因為線程都在條件等待pthread_mutex_lock(&pool->mtx);LL_ADD(job, pool->jobs);pthread_cond_signal(pool->cond);pthread_mutex_unlock(&pool->mtx);
}// // 測試函數
void debug_test()
{nThreadPool pool;const int NUM_THREADS = 10;const int NUM_TASKS = 1000;printf("Creating thread pool with %d workers...\n", NUM_THREADS);if (nThreadPoolCreate(&pool, NUM_THREADS) != 0) {fprintf(stderr, "Failed to create thread pool\n");return;}printf("Adding %d tasks to the pool...\n", NUM_TASKS);srand(time(NULL));for (int i = 0; i < NUM_TASKS; i++) {struct NJOB *job = (struct NJOB *)malloc(sizeof(struct NJOB));TaskData *data = (TaskData *)malloc(sizeof(TaskData));data->task_id = i;data->value = rand() % 100; // 隨機值0-99job->func = task_function;job->user_data = data;nThreadPoolPushTask(&pool, job);}// 等待所有任務完成printf("Waiting for tasks to complete...\n");sleep(3); // 簡單等待3秒// 銷毀線程池printf("Destroying thread pool...\n");nThreadPoolDestroy(&pool);printf("All done!\n");
}#if 1 //#if 0是完美注釋 #if 1即可運行
int main()
{debug_test();return 0;
}
#endif
互斥鎖
互斥鎖(Mutex)詳解
互斥鎖(Mutual Exclusion Lock,簡稱 Mutex)是多線程編程中用于保護共享資源不被同時訪問的核心同步機制。它的核心作用是確保在任何給定時刻,只有一個線程可以訪問被保護的資源或代碼區域。
互斥鎖的核心概念
1. 基本工作原理
2. 關鍵特性
- 互斥性:同一時刻只允許一個線程持有鎖
- 原子性:鎖的獲取和釋放操作是原子的(不可中斷)
- 阻塞性:未獲得鎖的線程會進入等待狀態
- 所有權:鎖由獲取它的線程獨占持有
互斥鎖的使用場景
場景類型 | 示例 | 互斥鎖的作用 |
---|---|---|
數據競爭 | 多個線程訪問同一變量 | 防止并發修改導致數據不一致 |
臨界區保護 | 文件I/O操作 | 確保操作序列的完整性 |
資源管理 | 數據庫連接池 | 控制有限資源的分配 |
狀態同步 | 緩存更新機制 | 保持狀態的一致性 |
互斥鎖的操作原語(以POSIX為例)
基本操作流程
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 線程函數
void* thread_func(void* arg) {pthread_mutex_lock(&mutex); // 1. 嘗試獲取鎖// 臨界區代碼 - 安全訪問共享資源pthread_mutex_unlock(&mutex); // 2. 釋放鎖return NULL;
}
鎖的類型
鎖類型 | 特性 | 適用場景 |
---|---|---|
普通鎖 | 默認類型,不可遞歸獲取 | 簡單臨界區保護 |
遞歸鎖 | 同一線程可多次獲取 | 遞歸函數或可重入代碼 |
檢錯鎖 | 檢測死鎖和重復鎖定 | 調試階段 |
自適應鎖 | 優化自旋等待時間 | 高競爭環境 |
互斥鎖的底層實現原理
1. 硬件支持
現代CPU提供原子指令實現鎖:
- Test-and-Set 指令
- Compare-and-Swap (CAS) 指令
- Load-Link/Store-Conditional (LL/SC) 指令
2. 用戶空間實現(Futex)
Linux的Futex(Fast Userspace Mutex)機制:
// 簡化版Futex實現
void futex_lock(int* lock) {while (atomic_compare_exchange(lock, 0, 1) != 0) {// 鎖已被占用,進入內核等待syscall(SYS_futex, lock, FUTEX_WAIT, 1, NULL, NULL, 0);}
}void futex_unlock(int* lock) {atomic_set(lock, 0);// 喚醒等待線程syscall(SYS_futex, lock, FUTEX_WAKE, 1, NULL, NULL, 0);
}
3. 內核支持
當鎖競爭激烈時,操作系統內核介入:
- 將等待線程放入阻塞隊列
- 在鎖釋放時調度等待線程
- 處理優先級反轉問題
互斥鎖的使用注意事項
1. 避免死鎖
死鎖產生的四個必要條件:
- 互斥條件
- 持有并等待
- 不可搶占
- 循環等待
預防策略:
- 固定加鎖順序
- 使用超時機制
- 死鎖檢測算法
2. 性能優化
技術 | 描述 | 效果 |
---|---|---|
細粒度鎖 | 為不同資源使用獨立鎖 | 減少競爭 |
讀寫鎖 | 區分讀/寫操作 | 提高讀并發性 |
無鎖編程 | CAS操作替代鎖 | 完全避免鎖開銷 |
鎖分段 | 將大資源分段加鎖 | 減少熱點競爭 |
3. 最佳實踐
// 推薦用法:RAII模式(資源獲取即初始化)
void critical_section() {LockGuard guard(&mutex); // 構造時加鎖// 臨界區代碼// ...} // 析構時自動解鎖(即使發生異常)
互斥鎖與其他同步機制對比
機制 | 特點 | 適用場景 |
---|---|---|
互斥鎖 | 獨占訪問,簡單直接 | 通用臨界區保護 |
自旋鎖 | 忙等待,不放棄CPU | 鎖持有時間極短 |
信號量 | 計數器,控制訪問數量 | 資源池管理 |
條件變量 | 基于條件等待/通知 | 生產者-消費者模式 |
讀寫鎖 | 區分讀/寫訪問權限 | 讀多寫少場景 |
互斥鎖在不同語言中的實現
C++ (std::mutex)
#include <mutex>std::mutex mtx;void safe_increment(int& value) {std::lock_guard<std::mutex> lock(mtx);value++;
}
Java (synchronized)
public class Counter {private int value;private final Object lock = new Object();public void increment() {synchronized(lock) {value++;}}
}
Python (threading.Lock)
import threadinglock = threading.Lock()
counter = 0def increment():global counterwith lock:counter += 1
互斥鎖的性能考量
性能指標
- 獲取延遲:從請求到獲取鎖的時間
- 吞吐量:單位時間處理的操作數
- 可擴展性:隨著線程數增加的性能變化
優化策略
- 減少臨界區大小:最小化加鎖代碼范圍
- 使用本地副本:在臨界區外準備數據
- 鎖消除:分析確定不需要的鎖
- 鎖粗化:合并相鄰的鎖操作
總結
互斥鎖是并發編程的基石,核心價值在于:
- 提供線程安全的共享資源訪問
- 確保數據一致性
- 維護程序正確性
正確使用互斥鎖需要:
- 精確識別需要保護的共享資源
- 保持臨界區盡可能短小
- 建立一致的加鎖順序
- 使用RAII模式管理鎖生命周期
- 在高競爭場景考慮替代方案
理解互斥鎖的底層原理和最佳實踐,是構建高性能、高可靠性并發系統的關鍵基礎。
C++程序從編譯到變為.exe文件發生了什么
將 C++ 源代碼編譯為 Windows 的 .exe
文件是一個多階段的復雜過程,涉及多個工具和轉換步驟。以下是詳細的編譯過程:
完整的編譯流程
詳細步驟解析
1. 預處理階段
- 輸入:
.cpp
源文件 + 頭文件 - 輸出:預處理后的源文件(通常為
.i
或.ii
) - 處理工具:預處理器(如
cpp
或編譯器內置預處理器) - 主要任務:
- 處理所有
#
開頭的預處理指令#include
→ 插入頭文件內容#define
→ 宏替換#if/#ifdef
→ 條件編譯
- 刪除所有注釋
- 添加行號標記(用于調試)
- 處理特殊宏(如
__LINE__
,__FILE__
)
- 處理所有
示例:
// main.cpp
#include <iostream>
#define PI 3.14159int main() {std::cout << "Value: " << PI; // 注釋
}
↓ 預處理后 ↓
// 插入<iostream>的全部內容
namespace std { ... } // 約數千行代碼
#line 1 "main.cpp"
int main() {std::cout << "Value: " << 3.14159;
}
2. 編譯階段
- 輸入:預處理后的源文件(
.i
) - 輸出:匯編代碼文件(
.s
或.asm
) - 處理工具:編譯器核心(如
g++
的編譯前端) - 主要任務:
- 詞法分析:將源代碼分解為 token(關鍵字、標識符、運算符等)
- 語法分析:構建抽象語法樹(AST)
- 語義分析:類型檢查、作用域分析
- 中間代碼生成:生成平臺無關的中間表示(如 LLVM IR)
- 優化:進行各種優化(常量折疊、死代碼消除等)
- 目標代碼生成:生成特定CPU架構的匯編代碼
生成的匯編示例(x86):
_main:push ebpmov ebp, esppush OFFSET FLAT:.LC0 ; "Value: "push 3.14159call __ZNSolsEf ; std::cout<<float...
3. 匯編階段
- 輸入:匯編代碼文件(
.s
) - 輸出:目標文件(
.obj
或.o
) - 處理工具:匯編器(如
as
或ml
) - 主要任務:
- 將匯編指令轉換為機器碼
- 生成目標文件格式(COFF 或 ELF)
- 創建符號表(函數/變量地址)
- 生成重定位信息(供鏈接器使用)
目標文件內容:
- 機器碼(二進制指令)
- 數據段(初始化的全局變量)
- BSS 段(未初始化的全局變量)
- 符號表
- 重定位記錄
4. 鏈接階段
- 輸入:多個目標文件(
.obj
) + 庫文件(.lib
) - 輸出:可執行文件(
.exe
) - 處理工具:鏈接器(如
ld
或link.exe
) - 主要任務:
- 符號解析:解決跨文件的函數/變量引用
- 重定位:合并所有目標文件,調整地址
- 解析外部依賴:鏈接標準庫和第三方庫
- 生成PE頭:創建Windows可執行文件結構
鏈接過程關鍵點:
PE文件結構:
├── DOS頭
├── PE文件頭
├── 節表
├── .text節(代碼)
├── .data節(初始化數據)
├── .rdata節(只讀數據)
├── .bss節(未初始化數據)
├── 導入表(依賴的DLL)
├── 資源節(圖標/版本信息)
└── 重定位表
完整工具鏈示例(GCC/MinGW)
# 1. 預處理(生成 main.ii)
g++ -E main.cpp -o main.ii# 2. 編譯(生成 main.s)
g++ -S main.ii -o main.s# 3. 匯編(生成 main.obj)
as main.s -o main.obj# 4. 鏈接(生成 program.exe)
g++ main.obj -o program.exe
或單步完成:
g++ main.cpp -o program.exe
關鍵概念詳解
1. 目標文件格式(COFF)
+-------------------+
| 文件頭 |
+-------------------+
| 節表 |
+-------------------+
| .text 節(代碼) |
+-------------------+
| .data 節(數據) |
+-------------------+
| .bss 節 |
+-------------------+
| 符號表 |
+-------------------+
| 重定位記錄 |
+-------------------+
| 調試信息 |
+-------------------+
2. PE文件結構
+-----------------------+
| DOS Stub |
+-----------------------+
| PE Header |
+-----------------------+
| Section Table |
+-----------------------+
| .text (Code) |
+-----------------------+
| .data (Initialized) |
+-----------------------+
| .rdata (Constants) |
+-----------------------+
| .idata (Imports) |
+-----------------------+
| .rsrc (Resources) |
+-----------------------+
| .reloc (Relocations) |
+-----------------------+
3. 動態鏈接 vs 靜態鏈接
特性 | 靜態鏈接 | 動態鏈接 |
---|---|---|
文件大小 | 較大(包含所有依賴庫) | 較小(僅包含引用) |
內存占用 | 每個程序獨立占用內存 | 共享庫節省內存 |
更新難度 | 需重新編譯整個程序 | 只需替換DLL文件 |
加載速度 | 較快(無運行時加載開銷) | 較慢(需加載DLL) |
示例 | -static 選項 | 默認方式 |
常見問題診斷
1. 鏈接錯誤示例
undefined reference to `someFunction()'
原因:
- 函數聲明但未定義
- 缺少鏈接庫
- 名稱修飾不匹配(C vs C++)
2. 運行時錯誤
The procedure entry point could not be located in DLL
原因:
- 運行時DLL版本不匹配
- 缺少依賴的DLL文件
- 導出函數簽名變更
3. 調試信息
- 編譯選項:
-g
生成調試信息 - 文件格式:
- Windows:PDB(Program Database)
- Linux:DWARF
- 調試工具:
- Windows:WinDbg, Visual Studio Debugger
- Linux:GDB
優化技術
-
編譯器優化:
-O1
:基礎優化(死代碼消除)-O2
:中級優化(循環展開)-O3
:激進優化(向量化)
-
LTO(鏈接時優化):
- 跨模塊內聯
- 全局數據流分析
- 使用:
g++ -flto
-
PGO(性能引導優化):
總結
從 C++ 源代碼到 .exe
文件的轉換過程:
- 預處理:文本級轉換(宏/頭文件展開)
- 編譯:高級語言 → 匯編語言
- 匯編:匯編語言 → 機器碼(目標文件)
- 鏈接:合并目標文件 + 解析外部依賴 → 可執行文件
理解這個過程有助于:
- 診斷編譯/鏈接錯誤
- 優化構建性能
- 進行底層調試
- 創建高效的構建系統
- 理解程序的內存布局和執行原理
pthread庫的常見用法以及使用函數
pthread_t
pthread_mutex_t
pthread_cond_t
pthread_mutex_lock
pthread_mutex_unlock
pthread_cond_wait
pthread_create
pthread_cond_broadcast
pthread_join
pthread_mutex_destroy
pthread_cond_destroy
pthread_cond_signal
pthread_mutex_lock
pthread_mutex_unlock
所有用到的pthread函數
pthread 庫詳解:常見用法與核心函數指南
一、pthread 庫概述
POSIX Threads (pthread) 是 Unix/Linux 系統上實現多線程編程的標準 API,提供了一套完整的線程創建、管理和同步機制。
核心特性:
- 跨平臺:支持所有 POSIX 兼容系統(Linux, macOS, BSD 等)
- 輕量級:線程創建和切換開銷遠小于進程
- 共享內存:同一進程的線程共享地址空間
- 豐富同步機制:提供互斥鎖、條件變量、讀寫鎖等
編譯鏈接:
gcc -pthread program.c -o program
二、核心線程管理函數
1. 線程創建 - pthread_create()
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);
thread
:輸出參數,存儲新線程IDattr
:線程屬性(NULL 為默認屬性)start_routine
:線程入口函數arg
:傳遞給線程函數的參數
示例:
void *print_message(void *message) {printf("%s\n", (char*)message);return NULL;
}int main() {pthread_t tid;char *msg = "Hello from thread!";pthread_create(&tid, NULL, print_message, msg);pthread_join(tid, NULL); // 等待線程結束return 0;
}
2. 線程終止 - pthread_exit()
void pthread_exit(void *retval);
- 用于線程內部主動退出
retval
:線程返回值(可由 pthread_join 獲取)
3. 線程等待 - pthread_join()
int pthread_join(pthread_t thread, void **retval);
- 阻塞調用線程,直到目標線程結束
retval
:接收目標線程的返回值
4. 線程分離 - pthread_detach()
int pthread_detach(pthread_t thread);
- 將線程標記為"可分離",結束后自動回收資源
- 分離線程不能被 join
5. 線程取消 - pthread_cancel()
int pthread_cancel(pthread_t thread);
- 請求取消指定線程
- 目標線程需要設置取消點(如 sleep, wait 等)
三、線程同步機制
1. 互斥鎖 (Mutex)
// 初始化(靜態)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 初始化(動態)
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);// 加鎖
int pthread_mutex_lock(pthread_mutex_t *mutex);// 嘗試加鎖(非阻塞)
int pthread_mutex_trylock(pthread_mutex_t *mutex);// 解鎖
int pthread_mutex_unlock(pthread_mutex_t *mutex);// 銷毀
int pthread_mutex_destroy(pthread_mutex_t *mutex);
使用模式:
pthread_mutex_t lock;void *thread_func(void *arg) {pthread_mutex_lock(&lock);// 臨界區代碼pthread_mutex_unlock(&lock);return NULL;
}
2. 條件變量 (Condition Variables)
// 初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);// 等待條件
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);// 定時等待
int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *abstime);// 發送信號(喚醒一個等待線程)
int pthread_cond_signal(pthread_cond_t *cond);// 廣播信號(喚醒所有等待線程)
int pthread_cond_broadcast(pthread_cond_t *cond);// 銷毀
int pthread_cond_destroy(pthread_cond_t *cond);
生產者-消費者示例:
pthread_mutex_t lock;
pthread_cond_t cond;
int count = 0;void *producer(void *arg) {while(1) {pthread_mutex_lock(&lock);count++;printf("Produced: %d\n", count);pthread_cond_signal(&cond); // 喚醒消費者pthread_mutex_unlock(&lock);sleep(1);}
}void *consumer(void *arg) {while(1) {pthread_mutex_lock(&lock);while(count == 0) {pthread_cond_wait(&cond, &lock); // 等待生產}count--;printf("Consumed: %d\n", count);pthread_mutex_unlock(&lock);}
}
3. 讀寫鎖 (Read-Write Lock)
// 初始化
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;// 讀鎖定(共享)
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);// 寫鎖定(獨占)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);// 解鎖
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
適用場景:
- 讀操作遠多于寫操作
- 允許多個讀線程同時訪問
四、線程屬性控制
1. 初始化/銷毀屬性
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
2. 常用屬性設置
// 設置分離狀態
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
// PTHREAD_CREATE_JOINABLE (默認) / PTHREAD_CREATE_DETACHED// 設置棧大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);// 設置調度策略
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
// SCHED_FIFO, SCHED_RR, SCHED_OTHER// 設置調度參數
int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);
示例:
pthread_attr_t attr;
pthread_attr_init(&attr);// 設置為分離線程
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 設置棧大小為2MB
pthread_attr_setstacksize(&attr, 2 * 1024 * 1024);pthread_t tid;
pthread_create(&tid, &attr, thread_func, NULL);pthread_attr_destroy(&attr);
五、線程特定數據 (Thread-Specific Data)
允許每個線程擁有自己的私有數據副本
// 創建鍵
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));// 刪除鍵
int pthread_key_delete(pthread_key_t key);// 設置鍵的值
int pthread_setspecific(pthread_key_t key, const void *value);// 獲取鍵的值
void *pthread_getspecific(pthread_key_t key);
示例:
pthread_key_t tls_key;void destructor(void *value) {free(value);
}void init_tls() {pthread_key_create(&tls_key, destructor);
}void *thread_func(void *arg) {// 為每個線程分配私有數據int *data = malloc(sizeof(int));*data = pthread_self(); // 示例值pthread_setspecific(tls_key, data);// 獲取私有數據int *my_data = pthread_getspecific(tls_key);printf("Thread %lu: my data = %d\n", (unsigned long)pthread_self(), *my_data);return NULL;
}
六、高級同步機制
1. 屏障 (Barriers)
// 初始化
int pthread_barrier_init(pthread_barrier_t *barrier,const pthread_barrierattr_t *attr,unsigned int count);// 等待
int pthread_barrier_wait(pthread_barrier_t *barrier);// 銷毀
int pthread_barrier_destroy(pthread_barrier_t *barrier);
使用場景:多線程同步點,所有線程到達屏障前阻塞
2. 自旋鎖 (Spinlocks)
// 初始化
pthread_spinlock_t spinlock;
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);// 加鎖/解鎖
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);
特點:忙等待鎖,適用于鎖持有時間極短的場景
七、最佳實踐與常見陷阱
1. 線程安全設計原則
- 優先使用不可變數據結構
- 限制共享數據范圍
- 使用RAII模式管理鎖
- 避免鎖嵌套(如必須,保持固定順序)
- 優先使用高級并發抽象(線程池、任務隊列)
2. 常見錯誤
// 錯誤1:返回棧上變量的指針
void *thread_func(void *arg) {int local = 42;return &local; // 錯誤!線程退出后局部變量被銷毀
}// 錯誤2:忘記解鎖
void critical_section() {pthread_mutex_lock(&lock);// 可能提前返回或拋出異常if(error) return; // 忘記解鎖!pthread_mutex_unlock(&lock);
}// 錯誤3:虛假喚醒處理不當
while(!condition) { // 必須用循環檢查條件pthread_cond_wait(&cond, &lock);
}
3. 調試技巧
- 使用
gdb
的info threads
查看線程狀態 - 使用
valgrind --tool=helgrind
檢測數據競爭 - 添加調試日志(帶線程ID):
printf("[Thread %lu] %s\n", (unsigned long)pthread_self(), message);
八、完整示例:線程池實現
#include <pthread.h>
#include <stdlib.h>#define THREAD_POOL_SIZE 4typedef struct {void (*task)(void *);void *arg;
} Task;typedef struct {pthread_mutex_t lock;pthread_cond_t task_cond;pthread_t threads[THREAD_POOL_SIZE];Task *task_queue;int queue_size;int queue_capacity;int shutdown;
} ThreadPool;void *worker_thread(void *arg) {ThreadPool *pool = (ThreadPool *)arg;while(1) {pthread_mutex_lock(&pool->lock);// 等待任務或關閉信號while(pool->queue_size == 0 && !pool->shutdown) {pthread_cond_wait(&pool->task_cond, &pool->lock);}if(pool->shutdown) {pthread_mutex_unlock(&pool->lock);pthread_exit(NULL);}// 取出任務Task task = pool->task_queue[--pool->queue_size];pthread_mutex_unlock(&pool->lock);// 執行任務task.task(task.arg);}return NULL;
}void thread_pool_init(ThreadPool *pool) {pthread_mutex_init(&pool->lock, NULL);pthread_cond_init(&pool->task_cond, NULL);pool->queue_capacity = 10;pool->queue_size = 0;pool->task_queue = malloc(pool->queue_capacity * sizeof(Task));pool->shutdown = 0;for(int i = 0; i < THREAD_POOL_SIZE; i++) {pthread_create(&pool->threads[i], NULL, worker_thread, pool);}
}void thread_pool_submit(ThreadPool *pool, void (*task)(void *), void *arg) {pthread_mutex_lock(&pool->lock);// 擴展任務隊列(如果需要)if(pool->queue_size == pool->queue_capacity) {pool->queue_capacity *= 2;pool->task_queue = realloc(pool->task_queue, pool->queue_capacity * sizeof(Task));}// 添加任務pool->task_queue[pool->queue_size].task = task;pool->task_queue[pool->queue_size].arg = arg;pool->queue_size++;pthread_cond_signal(&pool->task_cond);pthread_mutex_unlock(&pool->lock);
}void thread_pool_shutdown(ThreadPool *pool) {pthread_mutex_lock(&pool->lock);pool->shutdown = 1;pthread_cond_broadcast(&pool->task_cond);pthread_mutex_unlock(&pool->lock);for(int i = 0; i < THREAD_POOL_SIZE; i++) {pthread_join(pool->threads[i], NULL);}free(pool->task_queue);pthread_mutex_destroy(&pool->lock);pthread_cond_destroy(&pool->task_cond);
}
九、性能調優建議
-
線程數量優化
- CPU密集型:線程數 ≈ CPU核心數
- I/O密集型:線程數 ≈ (等待時間/計算時間 + 1) × CPU核心數
- 使用
sysconf(_SC_NPROCESSORS_ONLN)
獲取CPU核心數
-
鎖競爭優化
- 減小臨界區范圍
- 使用讀寫鎖替代互斥鎖
- 嘗試無鎖數據結構(atomic操作)
- 使用線程本地存儲減少共享
-
內存優化
- 設置合理的線程棧大小
- 避免false sharing(緩存行對齊)
- 使用內存池減少動態分配
十、跨平臺注意事項
-
Windows 兼容性:
- 使用 pthreads-win32 或 Windows Thread API
- 關鍵類型對應:
pthread_t
?HANDLE
pthread_mutex_t
?CRITICAL_SECTION
-
C++11 線程庫:
- 標準替代方案:
std::thread
,std::mutex
等 - 優點:跨平臺、類型安全
- 缺點:功能不如 pthread 豐富
- 標準替代方案:
總結
pthread 庫提供了強大靈活的多線程編程能力,核心要點:
- 理解線程生命周期管理(create/join/detach)
- 掌握同步機制(mutex/condvar/rwlock)
- 合理使用線程特定數據
- 遵循線程安全設計原則
- 重視資源管理和錯誤處理
通過合理使用 pthread,可以構建高性能、高并發的應用程序,充分利用多核處理器的計算能力。