線程等待
pthread_join()
pthread_join??是 Linux 系統中用于線程同步的重要函數,主要作用是等待指定線程結束并回收其資源。
基本功能
- 阻塞當前調用線程,直到目標線程執行結束。
- 回收目標線程的資源,避免產生“僵尸線程”。
- 可選地獲取目標線程的返回值。
函數原型
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
- ?thread?:需要等待的目標線程 ID(由 ?pthread_create??返回)。
- ?retval?:用于存儲目標線程的返回值(若不需要,可設為 ?NULL?)。
- 返回值:成功返回 0,失敗返回非 0 錯誤碼。
使用示例
#include <pthread.h>
#include <stdio.h>void *thread_func(void *arg) {printf("子線程執行\n");return (void *)100; // 子線程返回值
}int main() {pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);void *ret;pthread_join(tid, &ret); // 等待子線程結束,獲取返回值printf("子線程返回值:%d\n", (int)ret);return 0;
}
注意事項
- 每個線程只能被 ?pthread_join??一次,多次調用會出錯。
- 若不調用 ?pthread_join??且未設置線程分離(?pthread_detach?),線程結束后資源不會被回收,會成為僵尸線程。
- 若目標線程已結束,?pthread_join??會立即返回并回收資源。
線程退出
方式1
return?
方式2
pthread_exit()pthread_exit??是 Linux 中用于線程退出的函數,定義在 ?<pthread.h>??頭文件中,用于終止當前線程的執行并返回退出狀態。
基本用法
- 函數原型:?void pthread_exit(void *retval);?
- 參數 ?retval?:指向線程退出狀態的指針,其他線程可通過 ?pthread_join??獲取該值。
- 作用:終止調用線程,釋放線程資源(但不會自動釋放線程創建時分配的堆內存等,需手動管理)。
關鍵特點
- 僅終止當前線程:與 ?exit??不同,?pthread_exit??只結束調用它的線程,不影響進程中其他線程的執行。
- 退出狀態傳遞:通過 ?retval??傳遞的狀態需為全局變量或動態分配的內存(避免棧內存被釋放),否則其他線程可能獲取到無效值。
- 與 ?return??的區別:在線程函數中使用 ?return??與 ?pthread_exit??效果類似,但 ?pthread_exit??更靈活(可在函數中任意位置調用)。
示例
#include <pthread.h>
#include <stdio.h>void *thread_func(void *arg) {int *result = malloc(sizeof(int));*result = 100;pthread_exit(result); // 線程退出并返回結果
}int main() {pthread_t tid;void *ret;pthread_create(&tid, NULL, thread_func, NULL);pthread_join(tid, &ret); // 獲取線程退出狀態printf("線程返回值:%d\n", *(int*)ret);free(ret); // 釋放動態分配的內存return 0;
}
上述示例中,線程通過 ?pthread_exit??返回動態分配的結果,主線程通過 ?pthread_join??獲取并釋放內存。
方式3
pthread_cancel()(線程的取消)
pthread_cancel??是 Linux 中用于取消線程執行的函數,屬于 POSIX 線程庫(pthread)的一部分,其作用是請求終止指定的線程。
基本語法
#include <pthread.h>
int pthread_cancel(pthread_t thread);
- 參數 ?thread?:目標線程的 ID(由 ?pthread_create??返回)。
- 返回值:成功返回 0,失敗返回非 0 錯誤碼(如 ?ESRCH??表示線程不存在)。
核心特點
1.?請求而非強制終止
pthread_cancel??只是發送一個“取消請求”,并非立即終止線程。線程是否響應、何時響應,取決于其“取消狀態”和“取消類型”。
2.?取消狀態(可通過 ?pthread_setcancelstate??設置)
- ?PTHREAD_CANCEL_ENABLE?(默認):線程允許響應取消請求。
- ?PTHREAD_CANCEL_DISABLE?:線程忽略取消請求,直到狀態改為允許。
3.?取消類型(可通過 ?pthread_setcanceltype??設置,僅當狀態為允許時有效)
- ?PTHREAD_CANCEL_DEFERRED?(默認):線程在“取消點”(如 ?sleep?、?read??等系統調用)處響應請求。
- ?PTHREAD_CANCEL_ASYNCHRONOUS?:線程立即響應請求(較少使用,可能導致資源未釋放)。
注意事項
- 線程被取消后,資源(如鎖、內存)需通過“線程清理函數”(?pthread_cleanup_push?/?pthread_cleanup_pop?)釋放,避免泄漏。
- 并非所有函數都是取消點,可通過 ?pthread_testcancel??主動檢查取消請求(手動創建取消點)。
示例:通過 ?pthread_cancel??取消一個延遲響應的線程,需確保線程在取消點處被終止。
注意
線程間的通信不僅僅只可以交流傳輸字符串或整數
這段代碼展示了如何在多線程中通過指針傳遞自定義對象(?Request??和 ?Response?),實現線程間的數據交互。主要功能是創建一個子線程,計算從 ?start_??到 ?end_??的整數和,并通過 ?Response??對象返回結果。
代碼解析
1.?自定義類
- ?Request?:封裝線程的輸入參數(計算范圍 ?start_?/?end_?、線程名稱 ?threadname_?)。
- ?Response?:封裝線程的輸出結果(計算總和 ?result_?、狀態碼 ?exitcode_?)。
2.?線程函數 ?sumCount?
- 接收 ?Request*??類型的參數,解析輸入范圍并循環計算總和。
- 每次循環打印當前進度(線程名+當前計算的數字),并通過 ?usleep(100000)??暫停0.1秒(方便觀察過程)。
- 計算完成后,創建 ?Response??對象存儲結果,釋放 ?Request??資源,返回 ?Response*?。
3.?主線程 ?main?
- 創建 ?Request??對象并傳入子線程,通過 ?pthread_create??啟動線程。
- 調用 ?pthread_join??等待子線程結束,獲取返回的 ?Response*??并打印結果,最后釋放資源。
class Request{public:Request(int start, int end, const string &threadname): start_(start), end_(end), threadname_(threadname){}public:int start_;int end_;string threadname_;};class Response{public:Response(int result, int exitcode):result_(result),exitcode_(exitcode){}public:int result_; // 計算結果int exitcode_; // 計算結果是否可靠};void *sumCount(void *args) // 線程的參數和返回值,不僅僅可以用來進行傳遞一般參數,也可以傳遞對象!!{Request *rq = static_cast<Request*>(args); // Request *rq = (Request*)argsResponse *rsp = new Response(0,0);for(int i = rq->start_; i <= rq->end_; i++){cout << rq->threadname_ << " is runing, caling..., " << i << endl;rsp->result_ += i;usleep(100000);}delete rq;return rsp;}int main(){pthread_t tid;Request *rq = new Request(1, 100, "thread 1");pthread_create(&tid, nullptr, sumCount, rq);void *ret;pthread_join(tid, &ret);Response *rsp = static_cast<Response *>(ret);cout << "rsp->result: " << rsp->result_ << ", exitcode: " << rsp->exitcode_ << endl;delete rsp;return 0;}
線程及輕量化進程底層實現邏輯運用的是clone
clone?
?在 Linux 中,?clone??是一個系統調用,主要用于創建新的進程(或線程),與 ?fork??相比,它提供了更精細的控制能力,可以指定新進程與父進程共享哪些資源。
?
clone??的核心特點
- 靈活的資源共享:通過參數可以指定新進程是否共享父進程的內存空間、文件描述符表、信號處理等資源。例如,創建線程時通常會共享內存空間,而創建獨立進程時則不共享。
- 與 ?fork??的關系:?fork??可以看作是 ?clone??的一種特殊情況(?clone??省略部分參數時的簡化版),?fork??會復制父進程的幾乎所有資源,而 ?clone??可按需共享。
主要用途
- 創建線程(如 POSIX 線程 pthread 底層可能使用 ?clone?,共享內存空間)。
- 創建具有特定資源共享策略的進程,滿足特殊場景需求(如輕量級進程 LWP)。
簡單來說,?clone??是 Linux 中一個更底層、更靈活的進程/線程創建工具,通過控制資源共享粒度,適應不同的并發場景。
由于clone相對于用戶而言使用過于復雜,因此 Clone被包裝成庫供用戶使用,開發者將要調用的函數指針和棧區暴露出來給用戶使用,由于操作系統要對線程進行管理,而線程共用一個動態庫,因此副線程它主要存儲在共享區,而主線程及進程它是存放在內核的主線程棧中
TCB
在 Linux 中,TCB(Thread Control Block,線程控制塊) 是內核中用于管理線程狀態的核心數據結構,類似于進程的 PCB(Process Control Block),但專門用于線程。
TCB 的主要作用
- 存儲線程的基本信息,如線程 ID(TID)、狀態(運行、就緒、阻塞等)。
- 記錄線程的上下文(寄存器值、程序計數器等),用于線程切換時保存和恢復狀態。
- 關聯線程所屬的進程(進程 PCB),以及線程組信息。
- 管理線程的棧指針、信號掩碼、調度優先級等私有資源。
與 clone 的關系
當使用 ?clone??創建線程時(如指定 ?CLONE_THREAD?、?CLONE_VM??等標志),內核會為新線程創建一個 TCB,同時共享進程的部分資源(如內存空間)。TCB 是內核識別和調度線程的關鍵,確保每個線程能獨立被調度,同時與同進程其他線程協作。
簡單說,TCB 就是線程在內核中的“身份證”和“狀態檔案”,支撐線程的獨立運行和管理。
TID
在 Linux 中,TID(Thread ID,線程 ID) 是用于唯一標識線程的編號,類似于進程的 PID(Process ID),但專門針對線程。
TID 的特點
- 唯一性:系統中每個線程都有一個唯一的 TID,即使是同一進程內的不同線程,TID 也互不相同。
- 與 PID 的關系:在 Linux 中,線程本質上是輕量級進程(LWP),因此 TID 在內核中與 PID 共享同一編號空間(即 TID 也是一個“進程 ID”,但屬于線程級別的標識)。
- 線程組關聯:同一進程的所有線程屬于同一個線程組,線程組的領頭線程(通常是進程創建的第一個線程)的 TID 等于進程的 PID。
作用
- 內核通過 TID 識別和調度不同的線程。
- 用戶態可通過 ?gettid()??系統調用獲取當前線程的 TID,用于線程管理、調試等場景(如 ?ps -T??命令可查看進程內的線程 TID)。
例如,一個進程包含 3 個線程,它們會有各自不同的 TID,但共享同一個進程 PID(等于領頭線程的 TID)。
分散線程
Linux 中的分離線程(Detached Thread) 是一種特殊狀態的線程,核心特點是:
- 線程結束后會自動釋放資源(如棧、寄存器等),無需其他線程調用 ?pthread_join()??等待回收。
- 無法通過 ?pthread_join()??獲取其退出狀態。
主要用途
避免線程資源泄漏,適用于不需要等待其完成、也無需獲取結果的場景(如后臺日志打印、異步任務等)。
設置方式
1.?創建時指定:通過 ?pthread_attr_t??設置屬性為 ?PTHREAD_CREATE_DETACHED?。
2.?創建后設置:調用 ?pthread_detach(pthread_t thread)??函數將線程轉為分離狀態。
分離線程的核心是自動回收資源,簡化線程管理,避免手動回收的繁瑣。
pthread_detach??是 POSIX 線程庫中的一個函數,用于將指定線程設置為分離狀態(detached)。
核心作用
讓目標線程在結束時自動釋放所有資源(如棧空間、線程描述符等),無需其他線程通過 ?pthread_join??來等待或回收。
函數原型
#include <pthread.h>
int pthread_detach(pthread_t thread);
- 參數 ?thread?:要設置為分離狀態的線程 ID(由 ?pthread_create??返回)。
- 返回值:成功返回 0,失敗返回非 0 錯誤碼(如線程不存在)。
使用場景
適用于不需要獲取線程退出狀態、也無需等待其完成的場景(例如后臺服務線程、日志打印線程),避免資源泄漏。
注意點
- 線程一旦被分離,就無法再通過 ?pthread_join??獲取其狀態,調用會失敗。
- 可在線程創建后立即調用(通常由主線程或線程自身調用),也可在創建時通過屬性直接指定為分離狀態(更高效)。
簡單說,?pthread_detach??就是“告訴系統:這個線程結束后自己收拾干凈,不用別人管了”。??
線程私有化關鍵字__thread?
__thread??是 GCC 編譯器(GNU C 擴展)提供的關鍵字,用于聲明線程局部變量(Thread-Local Variables),實現線程空間私有化。它的作用是讓變量在每個線程中擁有獨立的副本,線程對變量的操作僅影響自身副本,不干擾其他線程。
主要特點
- 適用場景:主要用于 C/C++ 代碼,需配合 GCC 編譯器(或支持該擴展的編譯器,如 Clang)。
- 生命周期:變量的生命周期與線程一致,線程創建時初始化,線程結束時自動銷毀。
- 初始化限制:只能用常量初始化(不能用運行時動態計算的值)。
示例代碼
#include <stdio.h>
#include <pthread.h>
// 聲明線程局部變量,每個線程有獨立副本
__thread int var = 0;void* thread_func(void* arg) {int id = *(int*)arg;var = id; ?// 每個線程修改自己的副本printf("線程 %d 中的 var 值:%d\n", id, var);return NULL;
}int main() {pthread_t tid1, tid2;int id1 = 1, id2 = 2;pthread_create(&tid1, NULL, thread_func, &id1);pthread_create(&tid2, NULL, thread_func, &id2);pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}
?輸出會顯示兩個線程的 ?var??值分別為 1 和 2,互不干擾。
與 ?thread_local??的關系
C11 標準引入了 ?thread_local??關鍵字,作為線程局部變量的標準語法,功能與 ?__thread??類似。?__thread??是 GCC 的非標準擴展,而 ?thread_local??是跨編譯器的標準實現(需編譯器支持 C11 或 C++11 及以上標準)。在支持標準的情況下,更推薦使用 ?thread_local??以保證可移植性。