1. 線程創建:pthread_create()
pthread_create()
?是 POSIX 線程庫(pthread)中用于創建新線程的函數。調用該函數后系統就會啟動一個與主線程并發的線程,并使其跳轉到入口函數處執行。
#include <pthread.h>int pthread_create(pthread_t *thread, // 指向線程標識符的指針const pthread_attr_t *attr, // 線程屬性(通常設為 NULL 使用默認值)void *(*start_routine)(void*), // 線程執行的函數void *arg // 傳遞給線程函數的參數
);
參數說明:?
- pthread_t* thread:輸出型參數,存儲新創建線程的標識符(線程 ID)。
- const pthread_attr_t* attr:設置線程的屬性(如棧大小、調度策略等)。 常用值:NULL(使用默認屬性)。?
- void* (*)(void*) start_routine:新線程啟動后執行的函數(線程入口點)。 要求:必須返回 void*,且接受一個 void* 參數。
- void* arg:傳遞給 start_routine 的參數。 注意:若無需參數,可傳 NULL;若需傳遞多個參數,可封裝為結構體。
返回值:?
- 成功:返回 0。
- 失敗:返回錯誤碼(如 EAGAIN、EINVAL 等),但不設置 errno。
示例代碼:?
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <pthread.h>
using namespace std;void* start_routine(void* arg)
{cout << "start_routine: 線程創建成功" << endl;return nullptr;
}int main()
{pthread_t tid;int n = pthread_create(&tid, nullptr, start_routine, nullptr);if(n != 0){cout << "線程創建失敗: " << strerror(n) << endl;}while(true);return 0;
}
2. 線程等待:pthread_join()
pthread_join() 是 POSIX 線程庫中用于等待線程結束并回收其資源的函數。
主線程如何取得線程運行結束的返回值呢?我們可以使用pthread_join()函數來對指定線程進行等待,并獲取其返回值。和waitpid()函數一樣,調用這個函數會使主線程阻塞在調用處直到被等待的指定線程運行結束。
和多進程編程一樣,線程如果不進行等待回收,那么其就會一直保留其運行結果等信息,造成內存泄漏。除此之外,與多進程編程不一樣的是,一個進程的多個線程共享主線程的地址空間,一旦主線程退出,其創建的所有線程都會被強制終止,無論其是否執行完。
所以在上面的例子當中,線程啟動之后我們讓主線程陷入了死循環當中,避免其提前退出。
#include <pthread.h>int pthread_join(pthread_t thread, // 要等待的線程 IDvoid **retval // 指向線程返回值的指針(可選)
);
核心功能:
- 阻塞等待:調用 pthread_join() 的線程會暫停執行,直到目標線程終止。
- 資源回收:線程終止后,其占用的系統資源(如線程描述符、棧空間)會被釋放。 若不調用 pthread_join(),終止的線程會成為 “僵尸線程”,造成資源泄漏。
- 獲取返回值:通過 retval 參數獲取目標線程的返回值(start_routine 的返回值或 pthread_exit() 的參數)。?
參數說明:
- pthread_t thread:指定要等待的線程 ID(由 pthread_create() 返回)。
- void** retval:輸出型參數,存儲線程的返回值(即線程函數 start_routine 的返回值)。 若無需獲取返回值,可傳 NULL。
?返回值:
- 成功:返回 0。
- 失敗:返回錯誤碼(如 EDEADLK、ESRCH 等)。
示例代碼:
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <pthread.h>
using namespace std;void* start_routine(void* arg)
{cout << "start_routine: 線程創建成功" << endl;for(int i = 0; i < 5; i++){cout << "計數----->" << i << endl;sleep(1);}return (void*)10;
}int main()
{pthread_t tid;int n = pthread_create(&tid, nullptr, start_routine, nullptr);if(n != 0){cout << "線程創建失敗: " << strerror(n) << endl;}int result;int m = pthread_join(tid, (void**)&result);if(m != 0){cout << "線程等待失敗: " << strerror(m) << endl; }cout << "線程的返回值為: " << result << endl;return 0;
}
3. 線程終止
除主線程以外,線程的正常終止有三種情況:
- 從入口函數的return處返回
- 調用pthread_exit()函數退出(調用exit()函數會導致整個進程退出)
- 某個線程調用pthread_cancel()來終止指定線程
?3.1 pthread_exit()
#include <pthread.h>void pthread_exit(void *retval); // 無返回值,終止當前線程
核心功能:
- 終止線程執行:調用 pthread_exit() 的線程會立即停止執行,并釋放其占用的資源(如棧空間),但不會釋放整個進程的資源。
- 傳遞返回值:retval 作為返回值被傳遞給等待該線程的其他線程。
- 不影響其他線程:僅終止當前線程,不會影響進程中的其他線程或主線程。
參數說明:
- void* retval:線程的返回值,可通過 pthread_join() 的 retval 參數獲取。 若無需返回值,可傳 NULL。
3.2 pthread_self()
pthread_self()函數用于線程獲取自身的線程ID。
#include <pthread.h>pthread_t pthread_self(void); // 返回當前線程的 ID
返回調用該函數的線程的唯一標識符。
3.3?pthread_cancel()
pthread_cancel() 是 POSIX 線程庫中用于請求終止某一個線程的函數。
#include <pthread.h>int pthread_cancel(pthread_t thread); // 請求取消指定線程
?核心功能:
- 發送取消請求:pthread_cancel() 向目標線程發送一個 “取消請求”,而非強制終止。線程是否響應以及如何響應取決于其取消狀態和取消類型。
- 取消點:預定義的系統調用(如 sleep()、read()、write()、pthread_join() 等),線程在執行這些函數時會檢查并處理取消請求。
參數說明:
- pthread_t thread:要取消的線程 ID(由 pthread_create() 返回)。?
返回值:
- 成功:返回 0。
- 失敗:返回錯誤碼(如 ESRCH,表示線程 ID 不存在)。?
?示例代碼:
// 終止自己==pthread_exit()
pthread_cancel(pthread_self());
4. 線程分離:pthread_detach()
pthread_detach() 是 POSIX 線程庫中用于將線程設置為分離狀態的函數。
分離狀態的線程在終止后,會自動釋放其占用的系統資源(如線程描述符、棧空間),無需其他線程調用 pthread_join() 回收。
并且其他線程調用pthread_join()對分離狀態的線程進行回收是非法的,會導致未定義錯誤。
#include <pthread.h> int pthread_detach(pthread_t thread); // 設置線程為分離狀態
參數說明:
- pthread_t thread:要設置為分離狀態的線程 ID(由 pthread_create() 返回)。
返回值:
- 成功:返回 0。
- 失敗:返回錯誤碼(如 ESRCH、EINVAL 等)。?
注意事項:
- 分離狀態不可逆轉:一旦線程被設置為分離狀態,無法再變回 joinable 狀態。
- 返回值無法獲取:分離線程的返回值會被自動丟棄,不能通過 pthread_join() 獲取。
- 資源釋放的確定性:分離線程終止后,系統會立即回收其資源,無需等待其他線程操作。
- 錯誤處理:若對已終止的線程調用 pthread_detach(),可能返回 ESRCH。 若對已分離的線程重復調用 pthread_detach(),可能返回 EINVAL。?
示例代碼:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void* detached_thread(void* arg) {printf("分離線程開始運行...\n");sleep(2); // 模擬耗時操作printf("分離線程結束\n");return NULL;
}int main() {pthread_t thread_id;// 創建新線程if (pthread_create(&thread_id, NULL, detached_thread, NULL) != 0) {perror("線程創建失敗");return 1;}// 將線程設置為分離狀態if (pthread_detach(thread_id) != 0) {perror("設置分離狀態失敗");return 1;}printf("主線程繼續執行,不等待分離線程\n");// 主線程可以提前退出,分離線程仍會繼續執行sleep(1);printf("主線程退出\n");return 0;
}
// 分離自己
pthread_detach(pthread_self());
5. 線程標識符
在 Linux 系統中,pthread_t 類型的線程標識符(tid)本質上是一個輕量級進程(LWP)ID或指向線程控制塊的指針。
5.1 線程控制塊(TCB)
每個線程在內核中對應一個 task_struct 結構(與進程相同),但共享父進程的資源。
而在用戶空間中,經過pthread庫的包裝之后另外設置了一種數據結構來維護額外的線程數據,即TCB,包括如下控制信息:
- 線程狀態(運行、阻塞等)
- 線程棧地址和大小
- 信號掩碼
- 內核 LWP ID
?5.2 pthread_t類型的本質
在 Linux 中,pthread_t 的具體類型定義取決于實現:
- glibc/NPTL:pthread_t 通常是一個 struct pthread*,即指向線程控制塊的指針。
- 用戶可見性:pthread_t 對用戶是不透明的,只能通過 POSIX 線程 API 操作。