Linux線程概念和控制方式
Linux線程概念
Linux中線程如何理解
線程<=執(zhí)行流<=進程
Linux中的線程模擬進程實現(xiàn)(線程就是輕量級進程)
與獨立的進程相比,線程創(chuàng)建和銷毀的開銷較小,因為它們共享相同的內(nèi)存空間和資源。
線程是進程內(nèi)的執(zhí)行分支,線程的執(zhí)行粒度比進程要細(只需要運行一部分代碼)
我們認為線程是操作系統(tǒng)調(diào)度的基本單位(進程內(nèi)部執(zhí)行流資源)
進程:承擔分配系統(tǒng)資源的實體(分給線程)
Linux中用進程的內(nèi)核數(shù)據(jù)結(jié)構(gòu)模擬線程
- 在一個程序里的一個執(zhí)行路線就叫做線程(thread)。更準確的定義是:線程是“一個進程內(nèi)部的控制序列”
- 一切進程至少都有一個執(zhí)行線程
- 線程在進程內(nèi)部運行,本質(zhì)是在進程地址空間內(nèi)運行(任何執(zhí)行流都要有資源,地址空間是資源窗口)
- 在Linux系統(tǒng)中,在CPU眼中,看到的PCB都要比傳統(tǒng)的進程更加輕量化
- 透過進程虛擬地址空間,可以看到進程的大部分資源,將進程資源合理分配給每個執(zhí)行流,就形成了線程 執(zhí)行流

頁表也有寄存器,幫忙找到頁表
地址空間:進程的資源窗口(進程通過地址空間看到資源)
如何理解資源分配給線程
線程分配資源本質(zhì)就是分配地址空間范圍
- 虛擬地址不是一個整體(分為10+10+12)
- 頁表也不是一整塊,被拆成了三部分,第一個10查找第一個頁表(因為是全0——全1,直接充當了頁表的下標)
- 一級頁表(頁目錄)里存二級頁表地址
- 二級頁表存放頁框起始地址
- 前兩個找到頁框
- 12的那個部分在頁框內(nèi)進行索引(偏移量)找到制定物理內(nèi)存
訪問虛擬地址時,虛擬地址有沒有調(diào)用內(nèi)存:
1.查頁目錄時,2級頁表不存在(中間10位),沒用被加載到內(nèi)存就發(fā)生缺頁中斷,二級頁表和頁框沒有建立映射關(guān)系
2.內(nèi)存管理基本單位:4kb
- c/c++中任意一個變量只有一個地址(取第一個字節(jié)為起始地址找偏移量。我們的地址只拿第一個就可以拿到數(shù)據(jù)(即使數(shù)據(jù)好幾個字節(jié)),起始地址+類型=起始地址+偏移量
- cr2寄存器存放上次引起缺頁中斷異常的虛擬地址
- cr3寄存器存放頁目錄的地址(頁表創(chuàng)建好后可以沒有后邊兩個,但是頁目錄一定要有)
Linux線程周邊概念
- 創(chuàng)建線程只要創(chuàng)建pcb即可,只有最后一個pcb被干掉才會釋放
- 線程在切換時一定要有上下文(頁表地址空間不用切換)
- 線程執(zhí)行的本質(zhì):進程調(diào)度
- cache中的在進程切換時會更改
- 線程被創(chuàng)建時也有自己的時間片(來自進程)(不能重新分配)=》進程總時間片不變
線程的優(yōu)點
- 創(chuàng)建一個新線程的代價要比創(chuàng)建一個新進程小得多
- 與進程之間的切換相比,線程之間的切換需要操作系統(tǒng)做的工作要少很多
- 線程占用的資源要比進程少很多
- 能充分利用多處理器的可并行數(shù)量
- 在等待慢速I/O操作結(jié)束的同時,程序可執(zhí)行其他的計算任務(wù)
- 計算密集型應(yīng)用,為了能在多處理器系統(tǒng)上運行,將計算分解到多個線程中實現(xiàn)
- I/O密集型應(yīng)用,為了提高性能,將I/O操作重疊。線程可以同時等待不同的I/O操作。
線程的缺點
性能損失
- 一個很少被外部事件阻塞的計算密集型線程往往無法與共它線程共享同一個處理器。
- 如果計算密集型 線程的數(shù)量比可用的處理器多,那么可能會有較大的性能損失,這里的性能損失指的是增加了額外的 同步和調(diào)度開銷,而可用的資源不變。
健壯性降低
- 編寫多線程需要更全面更深入的考慮,在一個多線程程序里,因時間分配上的細微偏差或者因共享了 不該共享的變量而造成不良影響的可能性是很大的,換句話說線程之間是缺乏保護的。
缺乏訪問控制
- 進程是訪問控制的基本粒度,在一個線程中調(diào)用某些OS函數(shù)會對整個進程造成影響。
編程難度提高
- 編寫與調(diào)試一個多線程程序比單線程程序困難得多
線程異常
- 單個線程如果出現(xiàn)除零,野指針問題導(dǎo)致線程崩潰,進程也會隨著崩潰
- 線程是進程的執(zhí)行分支,線程出異常,就類似進程出異常,進而觸發(fā)信號機制,終止進程,進程終止,該進程內(nèi)的所有線程也就隨即退出
線程用途
- 合理的使用多線程,能提高CPU密集型程序的執(zhí)行效率
- 合理的使用多線程,能提高IO密集型程序的用戶體驗(如生活中我們一邊寫代碼一邊下載開發(fā)工具,就是多線程運行的一種表現(xiàn))
Linux進程VS線程
進程是資源分配的基本單位
線程是調(diào)度的基本單位
線程共享進程數(shù)據(jù),但也擁有自己的一部分數(shù)據(jù): 線程ID 一組寄存器 棧 errno 信號屏蔽字 調(diào)度優(yōu)先級
進程具有獨立上下文(獨立運行)和棧(運行時變量數(shù)據(jù)(臨時))(線程執(zhí)行不會出現(xiàn)錯亂)
進程的多個線程共享 同一地址空間,
進程和線程的關(guān)系如下圖:

Linux線程控制
線程創(chuàng)建
Linux中沒有很明確的線程概念,有輕量級進程概念,故沒有直接提供線程的系統(tǒng)調(diào)用接口
我們需要使用第三方的pthread庫

- pthread_t *thread:輸出型參數(shù)(thread id)
- const pthread_atte_t(線程的屬性)設(shè)為nullptr即可
- void*(*start_routine)(void*)新線程執(zhí)行入口函數(shù),將函數(shù)入口地址傳進來可以調(diào)用函數(shù)
- void *arg:創(chuàng)建線程成功,新線程回調(diào)線程函數(shù)的時候,需要參數(shù),這個參數(shù)就是給線程函數(shù)傳遞的
錯誤檢查: 傳統(tǒng)的一些函數(shù)是,成功返回0,失敗返回-1,并且對全局變量errno賦值以指示錯誤。 pthreads函數(shù)出錯時不會設(shè)置全局變量errno(而大部分其他POSIX函數(shù)會這樣做)。而是將錯誤代碼通 過返回值返回 pthreads同樣也提供了線程內(nèi)的errno變量,以支持其它使用errno的代碼。對于pthreads函數(shù)的錯誤, 建議通過返回值業(yè)判定,因為讀取返回值要比讀取線程內(nèi)的errno變量的開銷更小
LWP是系統(tǒng)層次概念,用戶只用知道線程id
ps -aL查看所有輕量級進程
示例代碼
#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;
void* threadrun(void* args)
{
while(1)
{
cout<<"new thread:"<<getpid()<<endl;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,threadrun,nullptr);
while(1)
{
cout<<"main thread"<<getpid()<<endl;
sleep(1);
}
return 0;
}
問題分析
輸出混亂的表現(xiàn):
- 輸出內(nèi)容混在一起(如"main thread201314new thread:")
- 換行符位置不正確
- 輸出順序不可預(yù)測
根本原因:
- 主線程和新線程同時向標準輸出(stdout)寫入數(shù)據(jù)
cout不是線程安全的,多個線程同時使用會導(dǎo)致輸出內(nèi)容交叉- 線程調(diào)度由操作系統(tǒng)決定,執(zhí)行順序不確定
具體解釋:
cout << "main thread" << getpid() << endl; 不是原子操作
它實際上分為多個步驟:
- 輸出"main thread"
- 調(diào)用getpid()
- 輸出PID值
- 輸出換行符
在這些步驟之間,另一個線程可能插入自己的輸出
在后續(xù)可以用互斥鎖保護輸出
線程等待
為什么需要線程等待? 已經(jīng)退出的線程,其空間沒有被釋放,仍然在進程的地址空間內(nèi)。 創(chuàng)建新的線程不會復(fù)用剛才退出線程的地址空間。
在多線程程序中,主線程最后退出是良好的編程實踐,主要原因包括:
- 資源管理:確保所有子線程完成資源釋放
- 結(jié)果收集:獲取子線程的執(zhí)行結(jié)果或狀態(tài)
- 程序穩(wěn)定性:避免子線程因主線程退出而意外終止
- 有序關(guān)閉:實現(xiàn)優(yōu)雅的進程終止流程

#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;
int g_val = 100;
void show(const string& name)
{
cout<<name<<"say:"<<"hello thread"<<endl;
}
void *threadrun(void *args)
{
const char *name = (const char*)args;
int cnt = 5;
while (true)
{
printf("%s, pid: %d, g_val: %d, &g_val: 0x%p\n", name,getpid(), g_val, &g_val);
sleep(1);
// int a = 10;
// a /= 0;
cnt--;
if(cnt == 0) break;
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,threadrun,nullptr);
// while(1)
// {
// cout<<"main thread"<<getpid()<<endl;
// sleep(1);
// }
sleep(5);
pthread_join(tid,nullptr);
cout<<"main thread quit"<<endl;
return 0;
}
任何變量傳參都會產(chǎn)生臨時變量
返回值的拿到要用二級指針,因為返回類型是void*

線程終止
1. 如果thread線程通過return返回,value_ ptr所指向的單元里存放的是thread線程函數(shù)的返回值。 2. 如果thread線程被別的線程調(diào)用pthread_ cancel異常終掉,value_ ptr所指向的單元里存放的是常數(shù) PTHREAD_ CANCELED。
3. 如果thread線程是自己調(diào)用pthread_exit終止的,value_ptr所指向的單元存放的是傳給pthread_exit的參 數(shù)。 4
. 如果對thread線程的終止狀態(tài)不感興趣,可以傳NULL給value_ ptr參數(shù)。

return
#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;
int g_val = 100;
void show(const string& name)
{
cout<<name<<"say:"<<"hello thread"<<endl;
}
void *threadrun(void *args)
{
const char *name = (const char*)args;
int cnt = 5;
while (true)
{
printf("%s, pid: %d, g_val: %d, &g_val: 0x%p\n", name,getpid(), g_val, &g_val);
sleep(1);
// int a = 10;
// a /= 0;
cnt--;
if(cnt == 0) break;
}
return (void*)1;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,threadrun,nullptr);
// while(1)
// {
// cout<<"main thread"<<getpid()<<endl;
// sleep(1);
// }
//sleep(5);
void *retval;
pthread_join(tid,&retval);
//cout<<"main thread quit"<<endl;
cout << "main thread quit ..., ret: " << (long long int)retval << endl;
return 0;
}
等待默認阻塞等待,不考慮異常情況(異常是進程考慮的)任何一個線程出異常整個進程都要被干掉,無法返回
exit是終止進程的,不能用來終止線程,任何一個線程調(diào)用exit直接整個進程退出了
可以用pthread_exit退出

void *threadrun(void *args)
{
const char *name = (const char*)args;
int cnt = 5;
while (true)
{
printf("%s, pid: %d, g_val: %d, &g_val: 0x%p\n", name,getpid(), g_val, &g_val);
sleep(1);
// int a = 10;
// a /= 0;
cnt--;
if(cnt == 0) break;
}
pthread_exit((void*)100);
}線程取消

#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;
int g_val = 100;
void show(const string& name)
{
cout<<name<<"say:"<<"hello thread"<<endl;
}
void *threadrun(void *args)
{
const char *name = (const char*)args;
int cnt = 5;
while (true)
{
printf("%s, pid: %d, g_val: %d, &g_val: 0x%p\n", name,getpid(), g_val, &g_val);
sleep(1);
// int a = 10;
// a /= 0;
cnt--;
if(cnt == 0) break;
}
//pthread_exit((void*)100);
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,threadrun,nullptr);
sleep(1);
pthread_cancel(tid);
return 0;
} 
線程的參數(shù)和返回值不僅可以傳遞一般參數(shù),也可以傳遞對象
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <string>
using namespace std;
class ThreadCalculator {
public:
// 請求結(jié)構(gòu)體
struct Request {
int start;
int end;
string threadname;
Request(int s, int e, const string &name)
: start(s), end(e), threadname(name) {}
};
// 響應(yīng)結(jié)構(gòu)體
struct Response {
int result;
int exitcode;
Response(int r = 0, int e = 0) : result(r), exitcode(e) {}
};
// 靜態(tài)線程函數(shù)
static void* threadFunc(void* arg) {
Request* req = static_cast<Request*>(arg);
Response* resp = new Response();
for(int i = req->start; i <= req->end; ++i) {
cout << req->threadname << " is running, calculating... " << i << endl;
resp->result += i;
usleep(100000); // 100ms延遲
}
delete req;
return resp;
}
// 啟動計算
Response calculate(int start, int end, const string& name) {
pthread_t tid;
Request* req = new Request(start, end, name);
pthread_create(&tid, nullptr, &ThreadCalculator::threadFunc, req);
void* ret;
pthread_join(tid, &ret);
Response* resp = static_cast<Response*>(ret);
Response result = *resp;
delete resp;
return result;
}
};
int main() {
ThreadCalculator calculator;
auto result = calculator.calculate(1, 100, "Worker Thread");
cout << "Calculation result: " << result.result
<< ", Exit code: " << result.exitcode << endl;
return 0;
}語言已經(jīng)把系統(tǒng)調(diào)用封裝了
// 目前,我們的原生線程,pthread庫,原生線程庫
// // C++11 語言本身也已經(jīng)支持多線程了 vs 原生線程庫
void threadrun()
{
while(true)
{
cout << "I am a new thead for C++" << endl;
sleep(1);
}
}
int main()
{
thread t1(threadrun);
t1.join();
return 0;
}線程id
pthread_ create函數(shù)會產(chǎn)生一個線程ID,存放在第一個參數(shù)指向的地址中。該線程ID和前面說的線程ID 不是一回事。
前面講的線程ID屬于進程調(diào)度的范疇。因為線程是輕量級進程,是操作系統(tǒng)調(diào)度器的最小單位,所以需要 一個數(shù)值來唯一表示該線程。
pthread_ create函數(shù)第一個參數(shù)指向一個虛擬內(nèi)存單元,該內(nèi)存單元的地址即為新創(chuàng)建線程的線程ID, 屬于NPTL線程庫的范疇。線程庫的后續(xù)操作,就是根據(jù)該線程ID來操作線程的。
線程庫NPTL提供了pthread_ self函數(shù),可以獲得線程自身的ID:

#include <iostream>
#include <string>
#include <pthread.h>
#include <cstdlib>
#include <unistd.h>
using namespace std;
std::string toHex(pthread_t tid)
{
char hex[64];
snprintf(hex, sizeof(hex), "%p", tid);
return hex;
}
void *threadRoutine(void *args)
{
while(true)
{
cout << "thread id: " << toHex(pthread_self()) << endl;
sleep(1);
}
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, threadRoutine, (void*)"thread 1");
cout << "main thread create thead done, new thread id : " << toHex(tid) << endl;
pthread_join(tid, nullptr);
return 0;
}線程的概念是庫給我們維護的,注定了線程庫要維護多個線程屬性集合,線程庫要管理這些線程
原始線程庫要加載到內(nèi)存中
每一個線程的庫級別的tcb的其實地址叫做線程的tid
除了主線程,其他線程的獨立棧都在共享區(qū),具體來說是pthread庫中,tid指向的用戶tcb中
tid指向的那一塊就是tcb
每個線程是個執(zhí)行流,為了不讓受干擾,每個執(zhí)行流要有自己的棧結(jié)構(gòu)

多線程
__thread編譯選項
只能定義內(nèi)置類型,可以在線程啟動前獲得想要的屬性,在線程內(nèi)直接獲取即可,不用再調(diào)系統(tǒng)調(diào)用了
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>
using namespace std;
// 使用__thread修飾的線程局部變量
__thread int g_val = 100;
void* threadRoutine(void* args) {
int thread_num = *(int*)args;
// 每個線程有自己的g_val副本
g_val += thread_num;
cout << "Thread " << thread_num << ": g_val = " << g_val
<< ", address: " << &g_val << endl;
return nullptr;
}
int main() {
vector<pthread_t> tids;
const int NUM = 3;
for (int i = 0; i < NUM; i++) {
pthread_t tid;
int* arg = new int(i);
pthread_create(&tid, nullptr, threadRoutine, arg);
tids.push_back(tid);
}
for (auto tid : tids) {
pthread_join(tid, nullptr);
}
cout << "Main thread g_val: " << g_val << endl;
return 0;
}全局變量是共享的:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>
using namespace std;
// 全局變量,所有線程共享
int g_val = 100;
void* threadRoutine(void* args) {
int thread_num = *(int*)args;
// 每個線程都會修改同一個全局變量
g_val += thread_num;
cout << "Thread " << thread_num << ": g_val = " << g_val
<< ", address: " << &g_val << endl;
sleep(1);
return nullptr;
}
int main() {
vector<pthread_t> tids;
const int NUM = 3;
for (int i = 0; i < NUM; i++) {
pthread_t tid;
int* arg = new int(i);
pthread_create(&tid, nullptr, threadRoutine, arg);
tids.push_back(tid);
sleep(1);
}
for (auto tid : tids) {
pthread_join(tid, nullptr);
}
cout << "Final g_val: " << g_val << endl;
return 0;
} 
每個線程有自己的棧空間
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
using namespace std;
// 全局變量,用于存儲線程棧變量的地址
vector<void*> g_stack_addresses;
void* threadRoutine(void* args) {
int thread_num = *(int*)args;
// 局部變量(棧變量)
int stack_var = thread_num * 100;
cout << "Thread " << thread_num << ": stack_var address = "
<< &stack_var << ", value = " << stack_var << endl;
// 保存棧變量地址到全局vector(僅用于演示,實際開發(fā)中要小心)
g_stack_addresses.push_back(&stack_var);
// 模擬線程工作
sleep(1);
// 注意:這里stack_var的地址在函數(shù)返回后將無效
// 只是演示每個線程有自己的??臻g
return nullptr;
}
int main() {
const int NUM = 3;
vector<pthread_t> tids;
// 主線程的棧變量
int main_stack_var = 999;
cout << "Main thread: stack_var address = "
<< &main_stack_var << endl;
// 創(chuàng)建子線程
for (int i = 0; i < NUM; i++) {
pthread_t tid;
int* arg = new int(i); // 動態(tài)分配,避免棧地址共享問題
pthread_create(&tid, nullptr, threadRoutine, arg);
tids.push_back(tid);
}
// 等待所有線程結(jié)束
for (auto tid : tids) {
pthread_join(tid, nullptr);
}
// 打印所有線程的棧變量地址
cout << "\nAll stack variable addresses:" << endl;
for (size_t i = 0; i < g_stack_addresses.size(); i++) {
cout << "Thread " << i << " stack_var @ " << g_stack_addresses[i] << endl;
}
return 0;
}主線程和每個子線程數(shù)據(jù)地址不同
但是棧與棧之間沒有秘密
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>
using namespace std;
// 全局指針,用于演示棧共享的危險性
int* shared_ptr = nullptr;
void* threadRoutine(void* args) {
int thread_num = *(int*)args;
int stack_var = thread_num * 10; // 棧變量
if (thread_num == 1) {
shared_ptr = &stack_var; // 線程1將自己的棧變量地址暴露出去
}
sleep(1); // 確保線程1先運行
if (thread_num == 2) {
// 線程2嘗試訪問線程1的棧變量(危險?。?
if (shared_ptr) {
cout << "Thread 2 accessing Thread 1's stack: " << *shared_ptr << endl;
}
}
return nullptr;
}
int main() {
pthread_t t1, t2;
int num1 = 1, num2 = 2;
pthread_create(&t1, nullptr, threadRoutine, &num1);
pthread_create(&t2, nullptr, threadRoutine, &num2);
pthread_join(t1, nullptr);
pthread_join(t2, nullptr);
return 0;
}線程分離
- 默認情況下,新創(chuàng)建的線程是joinable的,線程退出后,需要對其進行pthread_join操作,否則無法釋放資源,從而造成系統(tǒng)泄漏。
- 如果不關(guān)心線程的返回值,join是一種負擔,這個時候,我們可以告訴系統(tǒng),當線程退出時,自動釋放線程資源。
線程的分離可以由主線程來做也可以由線程自己分離
共享一部分資源但不是“一家人”了,出了問題由系統(tǒng)自動回收
示例代碼
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <pthread.h>
using namespace std;
#define NUM 3
// int *p = NULL;
// __thread int g_val = 100;
__thread unsigned int number = 0;
__thread int pid = 0;
struct threadData
{
string threadname;
};
// __thread threadData td;
string toHex(pthread_t tid)
{
char buffer[128];
snprintf(buffer, sizeof(buffer), "0x%lx", tid);
return buffer;
}
void InitThreadData(threadData *td, int number)
{
td->threadname = "thread-" + to_string(number); // thread-0
}
// 所有的線程,執(zhí)行的都是這個函數(shù)?
void *threadRoutine(void *args)
{
pthread_detach(pthread_self());
// int test_i = 0;
threadData *td = static_cast<threadData *>(args);
// if(td->threadname == "thread-2") p = &test_i;
string tid = toHex(pthread_self());
int pid = getpid();
int i = 0;
while (i < 10)
{
cout << "tid: " << tid << ", pid: " << pid << endl;
// cout << "pid: " << getpid() << ", tid : "
// << toHex(number) << ", threadname: " << td->threadname
// << ", g_val: " << g_val << " ,&g_val: " << &g_val <<endl;
sleep(1);
i++;
}
delete td;
return nullptr;
}
int main()
{
// 創(chuàng)建多線程!
vector<pthread_t> tids;
for (int i = 0; i < NUM; i++)
{
pthread_t tid;
threadData *td = new threadData;
InitThreadData(td, i);
pthread_create(&tid, nullptr, threadRoutine, td);
tids.push_back(tid);
//sleep(1);
}
sleep(1); // 確保復(fù)制成功
// for(auto i : tids)
// {
// pthread_detach(i);
// }
// cout << "main thread get a thread local value, val: " << *p << ", &val: " << p << endl;
for (int i = 0; i < tids.size(); i++)
{
int n = pthread_join(tids[i], nullptr);
printf("n = %d, who = 0x%lx, why: %s\n", n, tids[i], strerror(n));
}
return 0;
}
關(guān)鍵特性
| 特性 | 描述 |
|---|---|
| 自動資源回收 | 分離線程終止后系統(tǒng)自動回收其資源(線程ID、棧等) |
| 不可連接(join) | 分離后不能再使用pthread_join等待該線程 |
| 立即生效性 | 若線程已終止,分離操作會立即觸發(fā)資源回收 |
使用場景
- 不需要獲取線程返回值時
- 不關(guān)心線程何時結(jié)束時
- 需要避免僵尸線程(類似僵尸進程的概念)
注意事項
- 不要重復(fù)分離:對同一線程多次detach會導(dǎo)致未定義行為
- 分離后禁止join:嘗試join已分離線程會返回EINVAL錯誤
- 主線程退出影響:若主線程先退出,分離的子線程也會被強制終止
- 資源釋放時機:分離線程的資源釋放可能稍有延遲(由系統(tǒng)調(diào)度決定)
- 分離操作是不可逆的
分離 vs 非分離線程對比
| 特性 | 分離線程 | 非分離線程(默認) |
|---|---|---|
| 資源回收 | 自動 | 需手動pthread_join |
| 可連接性 | 不可join | 可join |
| 線程返回值 | 無法獲取 | 可通過pthread_join獲取 |
| 僵尸線程風(fēng)險 | 無 | 未join時會產(chǎn)生 |
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Linux下出現(xiàn)permission denied的解決辦法
這篇文章主要介紹了詳解Linux下出現(xiàn)permission denied的解決辦法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02
ubuntu lighttpd+webpy (fastcgi)配置方法
首先安裝 lighttpd 和 webpy,因為用 kpackagekit 做軟件管理,在安裝 webpy 的時候會自動安裝 flup2009-07-07
在Bash腳本中創(chuàng)建和使用數(shù)組方法總結(jié)
在本篇文章里小編給大家整理了關(guān)于在Bash腳本中創(chuàng)建和使用數(shù)組方法和相關(guān)知識點,需要的朋友們在學(xué)習(xí)下。2019-03-03
linux實現(xiàn)定時備份mysql數(shù)據(jù)庫的簡單方法
在本篇文章中我們給大家整理了一些關(guān)于linux實現(xiàn)定時備份mysql數(shù)據(jù)庫的簡單方法,有需要的朋友們可以學(xué)習(xí)下。2018-09-09

