POSIX線程創建與結束實戰:pthread_create、pthread_join、pthread_exit 完整示例
本文通過一個完整的「線程創建→運行→結束→回收」示例,詳細講解 pthread_create、pthread_exit、pthread_join 三大核心函數的參數、返回值、協作模式及常見錯誤。
一、核心API速覽
| 函數 | 作用 | 關鍵參數 | 返回值 |
pthread_create | 創建線程 | thread(輸出ID), attr(屬性,通常NULL), start_routine(入口函數), arg(傳參) | 0成功,錯誤碼失敗 |
pthread_exit | 主動結束線程 | retval(退出狀態指針) | 不返回(線程終止) |
pthread_join | 等待線程結束並回收資源 | thread(目標ID), retval(輸出退出狀態) | 0成功,錯誤碼失敗 |
二、完整示例代碼
#include
#include
#include
#include
// 全局變量:線程退出狀態、線程參數
int status = 0;
char param[] = "Thread function param";
// 線程入口函數
void *thread_func(void *arg) {
printf("Thread running, param: %s\n", (char*)arg);
printf("Old status: %d\n", status);
status = 3; // 修改全局狀態
pthread_exit(&status); // 退出,返回狀態地址
// 注意:此處代碼不會執行
return NULL;
}
int main() {
pthread_t tid; // 線程ID
int ret;
void *thread_ret; // 接收線程退出狀態
printf("Creating thread...\n");
// 1. 創建線程
ret = pthread_create(&tid, NULL, thread_func, (void*)param);
if (ret != 0) {
fprintf(stderr, "pthread_create failed: %d\n", ret);
return 1;
}
printf("Thread created, ID: %lu\n", (unsigned long)tid);
// 2. 主線程繼續做其他事...
sleep(1);
// 3. 等待線程結束,回收資源,獲取退出狀態
ret = pthread_join(tid, &thread_ret);
if (ret != 0) {
fprintf(stderr, "pthread_join failed: %d\n", ret);
return 1;
}
printf("Thread joined, returned status: %d\n", *(int*)thread_ret);
printf("Global status after join: %d\n", status);
return 0;
}
三、運行結果解析
Creating thread...
Thread running, param: Thread function param
Old status: 0
Thread joined, returned status: 3
Global status after join: 3
- 主線程調用
pthread_create,新線程開始執行 thread_func。
- 新線程修改全局
status,調用 pthread_exit(&status) 退出,將 &status 作為退出值。
- 主線程調用
pthread_join 阻塞等待,線程結束後解除阻塞,通過 thread_ret 獲取退出值指針,解引用得 3。
- 全局
status 也被修改為 3(共享內存可見性)。
四、關鍵細節與陷阱
1. 線程退出方式的區別
| 方式 | 說明 | 退出值獲取 |
pthread_exit(retval) | 主動退出,可指定退出值指針 | pthread_join 的 retval 參數 |
return retval | 從入口函數返回,等價於 pthread_exit | 同上 |
exit() / _exit() | 結束整個進程,所有線程強制終止 | 不可獲取單個線程退出值 |
主線程 return | 相當於 exit(),結束進程 | 其他線程未完成會被強制結束 |
鐵律:主線程必須 pthread_join 所有工作線程,或調用 pthread_exit 讓進程等待其他線程(但主線程退出值丟失)。
2. 退出值指針的有效性
// 錯誤:返回棧上局部變量地址
void *bad_func(void *arg) {
int local = 42;
pthread_exit(&local); // 危險!棧幀銷毀後地址失效
}
// 正確:返回堆內存、全局/靜態變量、或主線程傳入的緩衝區
void *good_func(void *arg) {
int *result = malloc(sizeof(int));
*result = 42;
pthread_exit(result); // 調用者需 free
}
3. 分離態線程
pthread_detach(tid) 或屬性設置 PTHREAD_CREATE_DETACHED。
- 分離態線程結束時自動回收資源,不可
pthread_join。
- 適用於「發射後不管」的後台任務。
4. 線程屬性 pthread_attr_t
- 棧大小(
pthread_attr_setstacksize,默認通常 8MB)。
- 分離態/可連接態。
- 調度策略/優先級(實時調度需特權)。
- 繼承調度屬性。
5. 常見錯誤
- 忘記鏈接
-lpthread → 鏈接錯誤。
- 傳遞棧上緩衝區給線程,主線程提前銷毀 → 線程讀到垃圾數據。
pthread_join 同一線程兩次 → 未定義行為。
- 線程函數簽名錯誤:必須是
void* (*)(void*)。
- 全局變量無同步保護 → 數據競態(示例中
status 為演示簡化,實戰需互斥量)。
五、編譯運行
gcc -Wall -Wextra -pthread -o thread_demo thread_demo.c
./thread_demo
掌握 create/exit/join 三部曲,是POSIX多線程編程的入門必修課。記住:創建傳參、退出傳值、加入回收,資源不洩漏,邏輯不競態。
- 上一篇文章: C語言優缺點深度剖析:為何它仍是系統編程王者
- 下一篇文章: 線程間通信?其實是共享內存:進程vs線程數據隔離對比實驗
Copyright ©
程式碼之家 All Rights Reserved