最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux線程概念和控制方式

 更新時(shí)間:2025年06月25日 09:45:47   作者:有沒有沒有重復(fù)的名字  
這篇文章主要介紹了Linux線程概念和控制方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Linux線程概念

Linux中線程如何理解

線程<=執(zhí)行流<=進(jìn)程

Linux中的線程模擬進(jìn)程實(shí)現(xiàn)(線程就是輕量級(jí)進(jìn)程)

與獨(dú)立的進(jìn)程相比,線程創(chuàng)建和銷毀的開銷較小,因?yàn)樗鼈児蚕硐嗤膬?nèi)存空間和資源。

線程是進(jìn)程內(nèi)的執(zhí)行分支,線程的執(zhí)行粒度比進(jìn)程要細(xì)(只需要運(yùn)行一部分代碼)

我們認(rèn)為線程是操作系統(tǒng)調(diào)度的基本單位(進(jìn)程內(nèi)部執(zhí)行流資源)

進(jìn)程:承擔(dān)分配系統(tǒng)資源的實(shí)體(分給線程)

Linux中用進(jìn)程的內(nèi)核數(shù)據(jù)結(jié)構(gòu)模擬線程

  • 在一個(gè)程序里的一個(gè)執(zhí)行路線就叫做線程(thread)。更準(zhǔn)確的定義是:線程是“一個(gè)進(jìn)程內(nèi)部的控制序列”
  • 一切進(jìn)程至少都有一個(gè)執(zhí)行線程
  • 線程在進(jìn)程內(nèi)部運(yùn)行,本質(zhì)是在進(jìn)程地址空間內(nèi)運(yùn)行(任何執(zhí)行流都要有資源,地址空間是資源窗口)
  • 在Linux系統(tǒng)中,在CPU眼中,看到的PCB都要比傳統(tǒng)的進(jìn)程更加輕量化
  • 透過進(jìn)程虛擬地址空間,可以看到進(jìn)程的大部分資源,將進(jìn)程資源合理分配給每個(gè)執(zhí)行流,就形成了線程 執(zhí)行流

頁表也有寄存器,幫忙找到頁表 

地址空間:進(jìn)程的資源窗口(進(jìn)程通過地址空間看到資源)

如何理解資源分配給線程

線程分配資源本質(zhì)就是分配地址空間范圍

  • 虛擬地址不是一個(gè)整體(分為10+10+12)
  • 頁表也不是一整塊,被拆成了三部分,第一個(gè)10查找第一個(gè)頁表(因?yàn)槭侨?——全1,直接充當(dāng)了頁表的下標(biāo))
  • 一級(jí)頁表(頁目錄)里存二級(jí)頁表地址
  • 二級(jí)頁表存放頁框起始地址
  • 前兩個(gè)找到頁框
  • 12的那個(gè)部分在頁框內(nèi)進(jìn)行索引(偏移量)找到制定物理內(nèi)存

訪問虛擬地址時(shí),虛擬地址有沒有調(diào)用內(nèi)存:

1.查頁目錄時(shí),2級(jí)頁表不存在(中間10位),沒用被加載到內(nèi)存就發(fā)生缺頁中斷,二級(jí)頁表和頁框沒有建立映射關(guān)系

2.內(nèi)存管理基本單位:4kb

  • c/c++中任意一個(gè)變量只有一個(gè)地址(取第一個(gè)字節(jié)為起始地址找偏移量。我們的地址只拿第一個(gè)就可以拿到數(shù)據(jù)(即使數(shù)據(jù)好幾個(gè)字節(jié)),起始地址+類型=起始地址+偏移量
  • cr2寄存器存放上次引起缺頁中斷異常的虛擬地址 
  • cr3寄存器存放頁目錄的地址(頁表創(chuàng)建好后可以沒有后邊兩個(gè),但是頁目錄一定要有)

Linux線程周邊概念

  • 創(chuàng)建線程只要?jiǎng)?chuàng)建pcb即可,只有最后一個(gè)pcb被干掉才會(huì)釋放
  • 線程在切換時(shí)一定要有上下文(頁表地址空間不用切換)
  • 線程執(zhí)行的本質(zhì):進(jìn)程調(diào)度
  • cache中的在進(jìn)程切換時(shí)會(huì)更改
  • 線程被創(chuàng)建時(shí)也有自己的時(shí)間片(來自進(jìn)程)(不能重新分配)=》進(jìn)程總時(shí)間片不變 

線程的優(yōu)點(diǎn)

  • 創(chuàng)建一個(gè)新線程的代價(jià)要比創(chuàng)建一個(gè)新進(jìn)程小得多
  • 與進(jìn)程之間的切換相比,線程之間的切換需要操作系統(tǒng)做的工作要少很多
  • 線程占用的資源要比進(jìn)程少很多
  • 能充分利用多處理器的可并行數(shù)量
  • 在等待慢速I/O操作結(jié)束的同時(shí),程序可執(zhí)行其他的計(jì)算任務(wù)
  • 計(jì)算密集型應(yīng)用,為了能在多處理器系統(tǒng)上運(yùn)行,將計(jì)算分解到多個(gè)線程中實(shí)現(xiàn)
  • I/O密集型應(yīng)用,為了提高性能,將I/O操作重疊。線程可以同時(shí)等待不同的I/O操作。

線程的缺點(diǎn)

性能損失

  • 一個(gè)很少被外部事件阻塞的計(jì)算密集型線程往往無法與共它線程共享同一個(gè)處理器。
  • 如果計(jì)算密集型 線程的數(shù)量比可用的處理器多,那么可能會(huì)有較大的性能損失,這里的性能損失指的是增加了額外的 同步和調(diào)度開銷,而可用的資源不變。

健壯性降低

  • 編寫多線程需要更全面更深入的考慮,在一個(gè)多線程程序里,因時(shí)間分配上的細(xì)微偏差或者因共享了 不該共享的變量而造成不良影響的可能性是很大的,換句話說線程之間是缺乏保護(hù)的。

缺乏訪問控制

  • 進(jìn)程是訪問控制的基本粒度,在一個(gè)線程中調(diào)用某些OS函數(shù)會(huì)對(duì)整個(gè)進(jìn)程造成影響。

編程難度提高

  • 編寫與調(diào)試一個(gè)多線程程序比單線程程序困難得多

線程異常

  • 單個(gè)線程如果出現(xiàn)除零,野指針問題導(dǎo)致線程崩潰,進(jìn)程也會(huì)隨著崩潰
  • 線程是進(jìn)程的執(zhí)行分支,線程出異常,就類似進(jìn)程出異常,進(jìn)而觸發(fā)信號(hào)機(jī)制,終止進(jìn)程,進(jìn)程終止,該進(jìn)程內(nèi)的所有線程也就隨即退出

線程用途

  • 合理的使用多線程,能提高CPU密集型程序的執(zhí)行效率
  • 合理的使用多線程,能提高IO密集型程序的用戶體驗(yàn)(如生活中我們一邊寫代碼一邊下載開發(fā)工具,就是多線程運(yùn)行的一種表現(xiàn))

Linux進(jìn)程VS線程

進(jìn)程是資源分配的基本單位

線程是調(diào)度的基本單位

線程共享進(jìn)程數(shù)據(jù),但也擁有自己的一部分?jǐn)?shù)據(jù): 線程ID 一組寄存器 棧 errno 信號(hào)屏蔽字 調(diào)度優(yōu)先級(jí)

進(jìn)程具有獨(dú)立上下文(獨(dú)立運(yùn)行)和棧(運(yùn)行時(shí)變量數(shù)據(jù)(臨時(shí)))(線程執(zhí)行不會(huì)出現(xiàn)錯(cuò)亂)

進(jìn)程的多個(gè)線程共享 同一地址空間,

進(jìn)程和線程的關(guān)系如下圖:

Linux線程控制 

線程創(chuàng)建

Linux中沒有很明確的線程概念,有輕量級(jí)進(jìn)程概念,故沒有直接提供線程的系統(tǒng)調(diào)用接口

我們需要使用第三方的pthread庫(kù)

  • pthread_t *thread:輸出型參數(shù)(thread id)
  • const pthread_atte_t(線程的屬性)設(shè)為nullptr即可
  • void*(*start_routine)(void*)新線程執(zhí)行入口函數(shù),將函數(shù)入口地址傳進(jìn)來可以調(diào)用函數(shù)
  • void *arg:創(chuàng)建線程成功,新線程回調(diào)線程函數(shù)的時(shí)候,需要參數(shù),這個(gè)參數(shù)就是給線程函數(shù)傳遞的 

錯(cuò)誤檢查: 傳統(tǒng)的一些函數(shù)是,成功返回0,失敗返回-1,并且對(duì)全局變量errno賦值以指示錯(cuò)誤。 pthreads函數(shù)出錯(cuò)時(shí)不會(huì)設(shè)置全局變量errno(而大部分其他POSIX函數(shù)會(huì)這樣做)。而是將錯(cuò)誤代碼通 過返回值返回 pthreads同樣也提供了線程內(nèi)的errno變量,以支持其它使用errno的代碼。對(duì)于pthreads函數(shù)的錯(cuò)誤, 建議通過返回值業(yè)判定,因?yàn)樽x取返回值要比讀取線程內(nèi)的errno變量的開銷更小 

LWP是系統(tǒng)層次概念,用戶只用知道線程id

ps -aL查看所有輕量級(jí)進(jìn)程

示例代碼

#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ù)測(cè)

根本原因

  • 主線程和新線程同時(shí)向標(biāo)準(zhǔn)輸出(stdout)寫入數(shù)據(jù)
  • cout不是線程安全的,多個(gè)線程同時(shí)使用會(huì)導(dǎo)致輸出內(nèi)容交叉
  • 線程調(diào)度由操作系統(tǒng)決定,執(zhí)行順序不確定

具體解釋

cout << "main thread" << getpid() << endl; 不是原子操作

它實(shí)際上分為多個(gè)步驟:

  • 輸出"main thread"
  • 調(diào)用getpid()
  • 輸出PID值
  • 輸出換行符

在這些步驟之間,另一個(gè)線程可能插入自己的輸出

在后續(xù)可以用互斥鎖保護(hù)輸出

線程等待 

為什么需要線程等待? 已經(jīng)退出的線程,其空間沒有被釋放,仍然在進(jìn)程的地址空間內(nèi)。 創(chuàng)建新的線程不會(huì)復(fù)用剛才退出線程的地址空間。

在多線程程序中,主線程最后退出是良好的編程實(shí)踐,主要原因包括:

  • 資源管理:確保所有子線程完成資源釋放
  • 結(jié)果收集:獲取子線程的執(zhí)行結(jié)果或狀態(tài)
  • 程序穩(wěn)定性:避免子線程因主線程退出而意外終止
  • 有序關(guān)閉:實(shí)現(xiàn)優(yōu)雅的進(jìn)程終止流程

#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;
}

任何變量傳參都會(huì)產(chǎn)生臨時(shí)變量

返回值的拿到要用二級(jí)指針,因?yàn)榉祷仡愋褪莢oid*

 線程終止

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

. 如果對(duì)thread線程的終止?fàn)顟B(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;
}

 

等待默認(rèn)阻塞等待,不考慮異常情況(異常是進(jìn)程考慮的)任何一個(gè)線程出異常整個(gè)進(jìn)程都要被干掉,無法返回

exit是終止進(jìn)程的,不能用來終止線程,任何一個(gè)線程調(diào)用exit直接整個(gè)進(jìn)程退出了

可以用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ù),也可以傳遞對(duì)象

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <string>

using namespace std;

class ThreadCalculator {
public:
    // 請(qǐng)求結(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;
    }

    // 啟動(dòng)計(jì)算
    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庫(kù),原生線程庫(kù)
// // C++11 語言本身也已經(jīng)支持多線程了 vs 原生線程庫(kù)
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ù)會(huì)產(chǎn)生一個(gè)線程ID,存放在第一個(gè)參數(shù)指向的地址中。該線程ID和前面說的線程ID 不是一回事。

前面講的線程ID屬于進(jìn)程調(diào)度的范疇。因?yàn)榫€程是輕量級(jí)進(jìn)程,是操作系統(tǒng)調(diào)度器的最小單位,所以需要 一個(gè)數(shù)值來唯一表示該線程。

pthread_ create函數(shù)第一個(gè)參數(shù)指向一個(gè)虛擬內(nèi)存單元,該內(nèi)存單元的地址即為新創(chuàng)建線程的線程ID, 屬于NPTL線程庫(kù)的范疇。線程庫(kù)的后續(xù)操作,就是根據(jù)該線程ID來操作線程的。

線程庫(kù)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;
}

 線程的概念是庫(kù)給我們維護(hù)的,注定了線程庫(kù)要維護(hù)多個(gè)線程屬性集合,線程庫(kù)要管理這些線程

原始線程庫(kù)要加載到內(nèi)存中

每一個(gè)線程的庫(kù)級(jí)別的tcb的其實(shí)地址叫做線程的tid

除了主線程,其他線程的獨(dú)立棧都在共享區(qū),具體來說是pthread庫(kù)中,tid指向的用戶tcb中

tid指向的那一塊就是tcb

每個(gè)線程是個(gè)執(zhí)行流,為了不讓受干擾,每個(gè)執(zhí)行流要有自己的棧結(jié)構(gòu)

多線程

__thread編譯選項(xiàng)

只能定義內(nèi)置類型,可以在線程啟動(dòng)前獲得想要的屬性,在線程內(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è)線程有自己的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è)線程都會(huì)修改同一個(gè)全局變量
    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;
}

 

 每個(gè)線程有自己的??臻g

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>

using namespace std;

// 全局變量,用于存儲(chǔ)線程棧變量的地址
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(僅用于演示,實(shí)際開發(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); // 動(dòng)態(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;
}

主線程和每個(gè)子線程數(shù)據(jù)地址不同 

但是棧與棧之間沒有秘密

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>

using namespace std;

// 全局指針,用于演示棧共享的危險(xiǎn)性
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先運(yùn)行
    
    if (thread_num == 2) {
        // 線程2嘗試訪問線程1的棧變量(危險(xiǎn)?。?
        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;
}

線程分離 

  • 默認(rèn)情況下,新創(chuàng)建的線程是joinable的,線程退出后,需要對(duì)其進(jìn)行pthread_join操作,否則無法釋放資源,從而造成系統(tǒng)泄漏。
  • 如果不關(guān)心線程的返回值,join是一種負(fù)擔(dān),這個(gè)時(shí)候,我們可以告訴系統(tǒng),當(dāng)線程退出時(shí),自動(dòng)釋放線程資源。

線程的分離可以由主線程來做也可以由線程自己分離

共享一部分資源但不是“一家人”了,出了問題由系統(tǒng)自動(dò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í)行的都是這個(gè)函數(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)鍵特性

特性描述
自動(dòng)資源回收分離線程終止后系統(tǒng)自動(dòng)回收其資源(線程ID、棧等)
不可連接(join)分離后不能再使用pthread_join等待該線程
立即生效性若線程已終止,分離操作會(huì)立即觸發(fā)資源回收

使用場(chǎng)景

  • 不需要獲取線程返回值時(shí)
  • 不關(guān)心線程何時(shí)結(jié)束時(shí)
  • 需要避免僵尸線程(類似僵尸進(jìn)程的概念)

注意事項(xiàng)

  • 不要重復(fù)分離:對(duì)同一線程多次detach會(huì)導(dǎo)致未定義行為
  • 分離后禁止join:嘗試join已分離線程會(huì)返回EINVAL錯(cuò)誤
  • 主線程退出影響:若主線程先退出,分離的子線程也會(huì)被強(qiáng)制終止
  • 資源釋放時(shí)機(jī):分離線程的資源釋放可能稍有延遲(由系統(tǒng)調(diào)度決定)
  • 分離操作是不可逆的

分離 vs 非分離線程對(duì)比

特性分離線程非分離線程(默認(rèn))
資源回收自動(dòng)需手動(dòng)pthread_join
可連接性不可join可join
線程返回值無法獲取可通過pthread_join獲取
僵尸線程風(fēng)險(xiǎn)未join時(shí)會(huì)產(chǎn)生

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Linux下PyTorch安裝教程

    Linux下PyTorch安裝教程

    這篇文章主要介紹了Linux下PyTorch安裝教程,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-03-03
  • Linux統(tǒng)計(jì)一個(gè)文件中特定字符個(gè)數(shù)的方法

    Linux統(tǒng)計(jì)一個(gè)文件中特定字符個(gè)數(shù)的方法

    今天小編就為大家分享一篇關(guān)于Linux統(tǒng)計(jì)一個(gè)文件中特定字符個(gè)數(shù)的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • 在Linux系統(tǒng)中使用Vim讀寫遠(yuǎn)程文件的命令詳解

    在Linux系統(tǒng)中使用Vim讀寫遠(yuǎn)程文件的命令詳解

    這篇文章主要介紹了在Linux系統(tǒng)中使用Vim讀寫遠(yuǎn)程文件的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Linux之PWM脈寬調(diào)制詳解

    Linux之PWM脈寬調(diào)制詳解

    Linux PWM技術(shù)通過調(diào)節(jié)占空比控制功率輸出,利用sysfs和/dev接口實(shí)現(xiàn),內(nèi)核自2.6.30版本支持,廣泛應(yīng)用于LED調(diào)光、電機(jī)控制、電源管理等領(lǐng)域,需注意頻率限制、精度與功率消耗等關(guān)鍵參數(shù)配置
    2025-09-09
  • 淺析Hyper-V安裝CentOS 8問題

    淺析Hyper-V安裝CentOS 8問題

    這篇文章主要介紹了Hyper-V安裝CentOS 8問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-10-10
  • 詳解nohup /dev/null 2>&1 含義的使用

    詳解nohup /dev/null 2>&1 含義的使用

    這篇文章主要介紹了詳解nohup /dev/null 2>&1 含義的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Linux中對(duì)lvm邏輯卷分區(qū)大小的調(diào)整教程(針對(duì)xfs與ext4不同文件系統(tǒng))

    Linux中對(duì)lvm邏輯卷分區(qū)大小的調(diào)整教程(針對(duì)xfs與ext4不同文件系統(tǒng))

    這篇文章主要給大家介紹了關(guān)于Linux中對(duì)lvm邏輯卷分區(qū)大小的調(diào)整教程(針對(duì)xfs與ext4不同文件系統(tǒng))的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • 詳解Linx awk 入門教程

    詳解Linx awk 入門教程

    AWK是一種處理文本文件的語言,是一個(gè)強(qiáng)大的文本分析工具。本文給大家?guī)砹薒inx awk 入門教程,感興趣的朋友跟隨小編一起看看吧
    2018-11-11
  • linux下統(tǒng)計(jì)appche站點(diǎn)IP訪問量的shell腳本

    linux下統(tǒng)計(jì)appche站點(diǎn)IP訪問量的shell腳本

    這篇文章主要介紹了linux下統(tǒng)計(jì)appche站點(diǎn)IP訪問量的幾種shell腳本以及執(zhí)行結(jié)果
    2014-06-06
  • 在 Linux 命令行發(fā)送郵件的 5 種方法(推薦)

    在 Linux 命令行發(fā)送郵件的 5 種方法(推薦)

    這篇文章主要介紹了在 Linux 命令行發(fā)送郵件的 5 種方法,本教程中包含了最流行的 5 個(gè)命令行郵件客戶端,你可以選擇其中一個(gè),需要的朋友可以參考下
    2019-12-12

最新評(píng)論

大埔县| 德安县| 曲靖市| 普定县| 九龙城区| 明水县| 封丘县| 舞阳县| 醴陵市| 田东县| 华容县| 石屏县| 鲜城| 隆尧县| 佛学| 子长县| 虞城县| 德清县| 鸡东县| 新宾| 城步| 湛江市| 卓资县| 遵义县| 嘉定区| 紫阳县| 扶绥县| 营口市| 綦江县| 张家港市| 慈溪市| 广河县| 色达县| 韶山市| 荃湾区| 桓台县| 忻城县| 临江市| 罗田县| 巩留县| 霍山县|