一、Linux線程概念
1.什么是線程
線程是進(jìn)程內(nèi)的一個(gè)執(zhí)行流。
我們知道,一個(gè)進(jìn)程會(huì)有對(duì)應(yīng)的PCB,虛擬地址空間,頁(yè)表以及映射的物理內(nèi)存。所以我們把這一個(gè)整體看做一個(gè)進(jìn)程,即進(jìn)程=內(nèi)核數(shù)據(jù)結(jié)構(gòu)+進(jìn)程對(duì)應(yīng)的代碼和數(shù)據(jù)。我們可以這樣看待虛存:虛擬內(nèi)存決定了進(jìn)程能夠看到的"資源"。因?yàn)槊恳粋€(gè)進(jìn)程都有對(duì)應(yīng)的虛擬內(nèi)存,所以進(jìn)程具有獨(dú)立性,從而進(jìn)程需要通信的前提是看到同一份資源。我們fork創(chuàng)建子進(jìn)程的時(shí)候,會(huì)將父進(jìn)程的PCB的內(nèi)容,進(jìn)程地址空間和頁(yè)表都給子進(jìn)程拷貝一份。那么我們可不可以創(chuàng)建多個(gè)PCB,而這些PCB使用同一個(gè)進(jìn)程地址空間和頁(yè)表,這樣就可以看到同一份資源了,這就是線程。
線程是進(jìn)程內(nèi)的一個(gè)執(zhí)行流,線程咋進(jìn)程內(nèi)運(yùn)行,線程在進(jìn)程的地址空間內(nèi)運(yùn)行,擁有該進(jìn)程的一部分資源。線程是CPU調(diào)度的基本單位。進(jìn)程是承擔(dān)系統(tǒng)資源的基本實(shí)體,內(nèi)部可以有一個(gè)或多個(gè)執(zhí)行流。因?yàn)槲覀兛梢酝ㄟ^(guò)虛擬地址空間+頁(yè)表的方式對(duì)進(jìn)程的資源進(jìn)行劃分,單個(gè)"進(jìn)程"(線程)執(zhí)行粒度,一定要比之前的進(jìn)程要細(xì)。
如果我們OS系統(tǒng)真的要專門設(shè)計(jì)線程的概念,OS系統(tǒng)未來(lái)就需要對(duì)線程進(jìn)行管理,就需要先描述,再組織,即一定要為線程設(shè)計(jì)專門的數(shù)據(jù)結(jié)構(gòu)表示線程對(duì)象TCB。但是線程和進(jìn)程一樣都需要被執(zhí)行,被調(diào)度(id,狀態(tài),優(yōu)先級(jí),上下文,?!?,二者十分相似,所以單純從線程調(diào)度角度,線程和進(jìn)程有很多的地方是重疊的。所以Linux工程師不想給"線程"專門設(shè)計(jì)對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),而是直接復(fù)用PCB,用PCB用來(lái)表示Linux內(nèi)部的"線程",所以在Linux中,進(jìn)程我們稱為輕量級(jí)進(jìn)程。而windows有單獨(dú)的TCB結(jié)構(gòu)
總結(jié):
1.Linux內(nèi)核中沒(méi)有真正意義是線程,Linux是用進(jìn)程的PCB來(lái)進(jìn)行模擬,是一種完全屬于自己的一套線程方案
2.站在CPU視角,每一個(gè)PCB,都可以稱之為輕量級(jí)進(jìn)程
3.Linux線程是CPU調(diào)度的基本單位,而進(jìn)程是承擔(dān)資源分配的基本單位
4.進(jìn)程用來(lái)整體申請(qǐng)資源,線程用來(lái)伸手向進(jìn)程要資源
5.在一個(gè)程序里的一個(gè)執(zhí)行路線就叫做線程(thread)。更準(zhǔn)確的定義是:線程是“一個(gè)進(jìn)程內(nèi)部的控制序列”
6.一切進(jìn)程至少都有一個(gè)執(zhí)行線程
7.線程在進(jìn)程內(nèi)部運(yùn)行,本質(zhì)是在進(jìn)程地址空間內(nèi)運(yùn)行
8.在Linux系統(tǒng)中,在CPU眼中,看到的PCB都要比傳統(tǒng)的進(jìn)程更加輕量化
9.透過(guò)進(jìn)程虛擬地址空間,可以看到進(jìn)程的大部分資源,將進(jìn)程資源合理分配給每個(gè)執(zhí)行流,就形成了線程執(zhí)行流
Linux內(nèi)核中沒(méi)有真正意義是線程,所以Linux便無(wú)法直接提供創(chuàng)建線程的系統(tǒng)調(diào)用接口,而只能給我們提供創(chuàng)建輕量級(jí)進(jìn)程的接口,但是操作系統(tǒng)只認(rèn)線程,用戶(程序員)也只認(rèn)線程,所以Linux在軟件層給我們提供了一個(gè)原生的線程庫(kù)。
任何Linux操作系統(tǒng),都必須默認(rèn)攜帶這個(gè)原生線程庫(kù)–用戶級(jí)線程庫(kù)
這里我們先見(jiàn)一見(jiàn)線程:
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <pthread.h>
using namespace std;
void *start_routine(void *args)
{
while (true)
{
cout << "我是新線程, 我正在運(yùn)行! " << endl;
sleep(1);
}
}
int main()
{
pthread_t tid;
int n = pthread_create(&tid, nullptr, start_routine, (void *)"thread one");
assert(0 == n);
(void)n;
while (true)
{
cout << "我是主線程, 我正在運(yùn)行!" << endl;
sleep(1);
}
return 0;
}
運(yùn)行結(jié)果:
我們可以看到,有兩個(gè)執(zhí)行流在運(yùn)行,這兩個(gè)執(zhí)行流的PID相同,而L不相同,其中LWP為light weight process為輕量級(jí)進(jìn)程ID,我們知道線程的CPU調(diào)度的基本單位,那么就需要唯一的標(biāo)識(shí)符,LWP就是線程的唯一標(biāo)識(shí)符。
CPU調(diào)度的時(shí)候以LWP為標(biāo)識(shí)符唯一的表示一個(gè)執(zhí)行流
當(dāng)我們只要一個(gè)執(zhí)行流的時(shí)候:
此時(shí)PID==LWP
注意:
線程一旦被創(chuàng)建,幾乎所有的資源都是被線程所共享的
線程也一定要有自己的私有資源:PCB私有,私有上下文結(jié)構(gòu),每一個(gè)線程都有自己獨(dú)立的棧結(jié)構(gòu)
線程切換和進(jìn)程切換相比,線程之間的切換需要操作系統(tǒng)做的工作要少很多:
1.進(jìn)程:切換頁(yè)表 && 虛擬地址空間 && 切換PCB &&上下文切換
2.線程:切換PCB &&上下文切換
3.線程切換,cache不用太更新,進(jìn)程切換需要全部更新。根據(jù)局部性原理我們知道,加載數(shù)據(jù)的時(shí)候會(huì)把需要訪問(wèn)的數(shù)據(jù)的周圍數(shù)據(jù)也加載進(jìn)去,所以執(zhí)行流運(yùn)行一段時(shí)間之后,cache里保存了許多熱點(diǎn)數(shù)據(jù),線程又使用相同的虛擬地址空間,所以線程切換的時(shí)候cache里面的數(shù)據(jù)就不用太更新,而進(jìn)程是使用不同的虛擬地址空間,所以進(jìn)程間切換的時(shí)候,需要重新加載數(shù)據(jù)
2.線程的優(yōu)缺點(diǎn)
線程的優(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ì)算密集型線程往往無(wú)法與共它線程共享同一個(gè)處理器。如果計(jì)算密集型線程的數(shù)量比可用的處理器多,那么可能會(huì)有較大的性能損失,這里的性能損失指的是增加了額外的同步和調(diào)度開銷,而可用的資源不變。
健壯性降低
編寫多線程需要更全面更深入的考慮,在一個(gè)多線程程序里,因時(shí)間分配上的細(xì)微偏差或者因共享了不該共享的變量而造成不良影響的可能性是很大的,換句話說(shuō)線程之間是缺乏保護(hù)的。
缺乏訪問(wèn)控制
進(jìn)程是訪問(wèn)控制的基本粒度,在一個(gè)線程中調(diào)用某些OS函數(shù)會(huì)對(duì)整個(gè)進(jìn)程造成影響。
編程難度提高
編寫與調(diào)試一個(gè)多線程程序比單線程程序困難得多
3.線程異常
單個(gè)線程如果出現(xiàn)除零,野指針問(wèn)題導(dǎo)致線程崩潰,進(jìn)程也會(huì)隨著崩潰,因?yàn)樾盘?hào)在整體發(fā)給進(jìn)程的
線程是進(jìn)程的執(zhí)行分支,線程出異常,就類似進(jìn)程出異常,進(jìn)而觸發(fā)信號(hào)機(jī)制,終止進(jìn)程,進(jìn)程終止,該進(jìn)程內(nèi)的所有線程也就隨即退出
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <pthread.h>
using namespace std;
void *start_routine(void *args)
{
while (true)
{
cout << "我是新線程, 我正在運(yùn)行! " << endl;
sleep(1);
int *p = nullptr;
*p = 0;
}
}
int main()
{
pthread_t tid;
int n = pthread_create(&tid, nullptr, start_routine, (void *)"thread one");
assert(0 == n);
(void)n;
while (true)
{
cout << "我是主線程, 我正在運(yùn)行!" << endl;
sleep(1);
}
return 0;
}
4.線程用途
合理的使用多線程,能提高CPU密集型程序的執(zhí)行效率
合理的使用多線程,能提高IO密集型程序的用戶體驗(yàn)(如生活中我們一邊寫代碼一邊下載開發(fā)工具,就是多線程運(yùn)行的一種表現(xiàn))
5.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)程的多個(gè)線程共享 同一地址空間,因此Text Segment、Data Segment都是共享的,如果定義一個(gè)函數(shù),在各線程
中都可以調(diào)用,如果定義一個(gè)全局變量,在各線程中都可以訪問(wèn)到,除此之外,各線程還共享以下進(jìn)程資源和環(huán)境:
文件描述符表
每種信號(hào)的處理方式(SIG_ IGN、SIG_ DFL或者自定義的信號(hào)處理函數(shù))
當(dāng)前工作目錄
用戶id和組id
進(jìn)程和線程的關(guān)系如下圖:
二、線程控制
1.線程創(chuàng)建
POSIX線程庫(kù)
與線程有關(guān)的函數(shù)構(gòu)成了一個(gè)完整的系列,絕大多數(shù)函數(shù)的名字都是以“pthread_”打頭的
要使用這些函數(shù)庫(kù),要通過(guò)引入頭文<pthread.h>
鏈接這些線程函數(shù)庫(kù)時(shí)要使用編譯器命令的“-lpthread”選項(xiàng)
創(chuàng)建線程函數(shù)接口 – pthread_create
功能:創(chuàng)建一個(gè)新的線程
原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(*start_routine)(void*), void *arg);
參數(shù)
thread:返回線程ID
attr:設(shè)置線程的屬性,attr為NULL表示使用默認(rèn)屬性
start_routine:是個(gè)函數(shù)地址,線程啟動(dòng)后要執(zhí)行的函數(shù)
arg:傳給線程啟動(dòng)函數(shù)的參數(shù)
返回值:成功返回0;失敗返回錯(cuò)誤碼
錯(cuò)誤檢查:
傳統(tǒng)的一些函數(shù)是,成功返回0,失敗返回-1,并且對(duì)全局變量errno賦值以指示錯(cuò)誤。
pthreads函數(shù)出錯(cuò)時(shí)不會(huì)設(shè)置全局變量errno(而大部分其他POSIX函數(shù)會(huì)這樣做)。而是將錯(cuò)誤代碼通過(guò)返回值返回
pthreads同樣也提供了線程內(nèi)的errno變量,以支持其它使用errno的代碼。對(duì)于pthreads函數(shù)的錯(cuò)誤,建議通過(guò)返回值業(yè)判定,因?yàn)樽x取返回值要比讀取線程內(nèi)的errno變量的開銷更小
我們這里創(chuàng)建10個(gè)進(jìn)程,打印相關(guān)信息,主要是線程的tid
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <vector>
#include <pthread.h>
using namespace std;
#define NUM 10
class ThreadData
{
public:
int number;
pthread_t tid;
char namebuffer[64];
};
void *start_routine(void *args)
{
ThreadData *td = static_cast<ThreadData *>(args);
int cnt = 10;
while (cnt)
{
cout << "new thread create success, name: " << td->namebuffer << " cnt: " << cnt-- << endl;
sleep(1);
}
return nullptr;
}
int main()
{
vector<ThreadData *> threads;
for (int i = 0; i < NUM; i++)
{
ThreadData *td = new ThreadData();
td->number = i + 1;
snprintf(td->namebuffer, sizeof(td->namebuffer), "thread:%d", i + 1);
pthread_create(&td->tid, nullptr, start_routine, td);
threads.push_back(td);
sleep(1);
}
for (auto &iter : threads)
{
cout << "create thread: " << iter->namebuffer << " : " << iter->tid << " suceesss" << endl;
}
return 0;
}
tid這么大的數(shù)字是什么呢,其實(shí)它是進(jìn)程地址空間上的一個(gè)地址
我們知道,Linux中的線程使用的是原生線程庫(kù),那么,在原生線程庫(kù)中就可能會(huì)存在多個(gè)線程,此時(shí)就需要對(duì)線程進(jìn)行管理,管理的方法是先描述再組織。在Linux中,用戶級(jí)線程,用戶關(guān)系的線程屬性在庫(kù)中,內(nèi)核中提供執(zhí)行流的調(diào)度。Linux用戶級(jí)線程:內(nèi)核輕量級(jí)進(jìn)程 = 1 : 1。
那么用戶級(jí)線程的tid究竟是什么呢,是庫(kù)中描述線程結(jié)構(gòu)體的起始地址。所以pthread_t 到底是什么類型呢?取決于實(shí)現(xiàn)。對(duì)于Linux目前實(shí)現(xiàn)的NPTL實(shí)現(xiàn)而言,pthread_t類型的線程ID,本質(zhì)就是一個(gè)進(jìn)程地址空間上的一個(gè)地址。
每一個(gè)結(jié)構(gòu)體就相當(dāng)于一個(gè)線程控制塊TCB,然后再使用數(shù)組進(jìn)行管理,就完成了對(duì)線程的管理
線程ID及進(jìn)程地址空間布局
pthread_ create函數(shù)會(huì)產(chǎn)生一個(gè)線程ID,存放在第一個(gè)參數(shù)指向的地址中。該線程ID和前面說(shuō)的線程ID不是一回事。
前面講的線程ID屬于進(jìn)程調(diào)度的范疇。因?yàn)榫€程是輕量級(jí)進(jìn)程,是操作系統(tǒng)調(diào)度器的最小單位,所以需要一個(gè)數(shù)值來(lái)唯一表示該線程。
pthread_ create函數(shù)第一個(gè)參數(shù)指向一個(gè)虛擬內(nèi)存單元,該內(nèi)存單元的地址即為新創(chuàng)建線程的線程ID,屬于NPTL線程庫(kù)的范疇。線程庫(kù)的后續(xù)操作,就是根據(jù)該線程ID來(lái)操作線程的。
線程庫(kù)NPTL提供了pthread_ self函數(shù),可以獲得線程自身的ID:
獲取線程ID函數(shù)接口–pthread_self
#include <pthread.h>
pthread_t pthread_self(void);
返回值是線程的id
2.線程終止
如果需要只終止某個(gè)線程而不終止整個(gè)進(jìn)程,可以有三種方法:
1.從線程函數(shù)return。這種方法對(duì)主線程不適用,從main函數(shù)return相當(dāng)于調(diào)用exit。
2.線程可以調(diào)用pthread_ exit終止自己。
3.一個(gè)線程可以調(diào)用pthread_ cancel終止同一進(jìn)程中的另一個(gè)線程。
1.從線程函數(shù)return終止線程
從線程函數(shù)return之后終止進(jìn)程就相當(dāng)于函數(shù)調(diào)用完畢,線程終止
void *start_routine(void *args)
{
string name = static_cast<char*>(args);
int cnt = 10;
while (cnt)
{
cout << "thread name: " << name << " cnt: " << cnt-- << endl;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,start_routine,(void*)"thread one");
pthread_join(tid,nullptr);
return 0;
}
2.調(diào)用pthread_ exit終止線程
pthread_exit函數(shù)
功能:線程終止
原型
void pthread_exit(void *value_ptr);
參數(shù)
value_ptr:value_ptr不要指向一個(gè)局部變量。
返回值:無(wú)返回值,跟進(jìn)程一樣,線程結(jié)束的時(shí)候無(wú)法返回到它的調(diào)用者(自身)
需要注意,pthread_exit或者return返回的指針?biāo)赶虻膬?nèi)存單元必須是全局的或者是用malloc分配的,不能在線程函數(shù)的棧上分配,因?yàn)楫?dāng)其它線程得到這個(gè)返回指針時(shí)線程函數(shù)已經(jīng)退出了。
void *start_routine(void *args)
{
string name = static_cast<char*>(args);
int cnt = 10;
while (cnt)
{
cout << "thread name: " << name << " cnt: " << cnt-- << endl;
if(cnt<5)
{
pthread_exit(nullptr);
}
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,start_routine,(void*)"thread one");
while (true)
{
cout << "new thread create success, name: main thread" << endl;
sleep(1);
}
pthread_join(tid,nullptr);
return 0;
}
為什么我們不直接只有exit結(jié)束呢,而還要提供一個(gè)pthread_exit函數(shù)來(lái)終止線程,因?yàn)閑xit是通過(guò)發(fā)送信號(hào)的方式來(lái)終止的,但是信號(hào)是整體發(fā)給進(jìn)程的,如果使用exit的話,主線程和新線程都會(huì)退出
3.調(diào)用pthread_ cancel終止線程
pthread_cancel函數(shù)
功能:取消一個(gè)執(zhí)行中的線程
原型
int pthread_cancel(pthread_t thread);
參數(shù)
thread:線程ID
返回值:成功返回0;失敗返回錯(cuò)誤碼
3.線程等待
為什么需要線程等待?
線程也是需要等待的,如果不進(jìn)行等待,那么就會(huì)像僵尸進(jìn)程一樣,導(dǎo)致內(nèi)存泄漏。
所以線程也必須進(jìn)行等待:1.獲取新線程的退出信息 2.回收新線程對(duì)應(yīng)的PCB等內(nèi)核資源,防止內(nèi)存泄漏
已經(jīng)退出的線程,其空間沒(méi)有被釋放,仍然在進(jìn)程的地址空間內(nèi)。
創(chuàng)建新的線程不會(huì)復(fù)用剛才退出線程的地址空間。
pthread_join函數(shù)接口
功能:等待線程結(jié)束
原型
int pthread_join(pthread_t thread, void **value_ptr);
參數(shù)
thread:線程ID
value_ptr:它指向一個(gè)指針,后者指向線程的返回值
返回值:成功返回0;失敗返回錯(cuò)誤碼
調(diào)用該函數(shù)的線程將掛起等待,直到id為thread的線程終止。thread線程以不同的方法終止,通過(guò)pthread_join得到的
我們發(fā)現(xiàn)value_ptr的類型是二級(jí)指針,而start_routine函數(shù)的返回值是void*,那么會(huì)不會(huì)start_routine的返回值就是線程的退出信息呢,答案是是的。本質(zhì)是從庫(kù)中獲取執(zhí)行線程的退出結(jié)果
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <vector>
#include <pthread.h>
using namespace std;
#define NUM 10
class ThreadData
{
public:
int number;
pthread_t tid;
char namebuffer[64];
};
class returnCode
{
public:
int exit_code;
int exit_result;
};
void *start_routine(void *args)
{
ThreadData *td = static_cast<ThreadData *>(args);
int cnt = 10;
while (cnt)
{
cout << "new thread create success, name: " << td->namebuffer << " cnt: " << cnt-- << endl;
sleep(1);
}
return (void*)td->number;
}
int main()
{
vector<ThreadData *> threads;
for (int i = 0; i < NUM; i++)
{
ThreadData *td = new ThreadData();
td->number = i + 1;
snprintf(td->namebuffer, sizeof(td->namebuffer), "thread:%d", i + 1);
pthread_create(&td->tid, nullptr, start_routine, td);
threads.push_back(td);
sleep(1);
}
for (auto &iter : threads)
{
void *ret = nullptr;
int n = pthread_join(iter->tid, (void **)&ret);
assert(n == 0);
cout << "join : " << iter->namebuffer << " sucess,exit_code: " << (long long)ret << endl;
delete iter;
}
return 0;
}
我們可以返回假的地址,整數(shù),對(duì)空間的地址,對(duì)象的地址,棧上的地址都能夠返回。
class ThreadReturn
{
public:
int exit_code;
int exit_result;
};
return (void *)td->number; // warning, void *ret = (void*)td->number;
return (void *)100;
pthread_exit((void *)111); // 既然假的地址,整數(shù)都能被外部拿到,那么如何返回的是,堆空間的地址呢?對(duì)象的地址呢?
ThreadReturn *tr = new ThreadReturn();
tr->exit_code = 1;
tr->exit_result = 100;
ThreadReturn tr; // 在棧上開辟的空間 return &tr;
return (void *)100; // 右值
我們需要注意的是:
我們使用一個(gè)void* ret來(lái)接收返回值,在函數(shù)pthread_join(tid,(void**)&ret)傳遞ret的地址進(jìn)去,在start_routine函數(shù)中返回的是(void*)100;所以void** p = &ret; 而(void*) x = (void*)100;那么*p = x。
所以ret = x。
終止?fàn)顟B(tài)是不同的,總結(jié)如下:
1.如果thread線程通過(guò)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ù)。
為什么沒(méi)有見(jiàn)到,線程退出的時(shí)候,對(duì)應(yīng)的退出信號(hào)??? 線程出異常,收到信號(hào),整個(gè)進(jìn)程都會(huì)退出!
pthread_join:默認(rèn)就認(rèn)為函數(shù)會(huì)調(diào)用成功!不考慮異常問(wèn)題,異常問(wèn)題是你進(jìn)程該考慮的問(wèn)題!
4.線程分離
線程是可以等待的,等待的時(shí)候,join是阻塞式等待,如果我們不想等待呢。我們可以將線程設(shè)置為分離狀態(tài)。
默認(rèn)情況下,新創(chuàng)建的線程是joinable的,線程退出后,需要對(duì)其進(jìn)行pthread_join操作,否則無(wú)法釋放資源,從而造成系統(tǒng)泄漏。
如果不關(guān)心線程的返回值,join是一種負(fù)擔(dān),這個(gè)時(shí)候,我們可以告訴系統(tǒng),當(dāng)線程退出時(shí),自動(dòng)釋放線程資源。
pthread_detach函數(shù)接口
功能:線程分離
原型
int pthread_detach(pthread_t thread);
thread:線程ID
返回值:成功返回0;失敗返回錯(cuò)誤碼
可以是線程組內(nèi)其他線程對(duì)目標(biāo)線程進(jìn)行分離,也可以是線程自己分離:
pthread_detach(pthread_self());
joinable和分離是沖突的,一個(gè)線程不能既是joinable又是分離的。
void *start_routine(void *args)
{
string threadname = static_cast<char *>(args);
pthread_detach(pthread_self()); // 設(shè)置為分離狀態(tài)
int cnt = 5;
while (cnt--)
{
cout << threadname << " running " << endl;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, start_routine, (void *)"thread one");
cout << "main thread running..." << endl;
//一個(gè)線程默認(rèn)是joinable,如果設(shè)置的分離狀態(tài),就不能夠進(jìn)行等待了
int n = pthread_join(tid, nullptr);
cout << "result: " << n << " : " << strerror(n) << endl;
return 0;
}
線程未分離之前:
這是正常的退出。
將線程進(jìn)行分離:
此時(shí)就出現(xiàn)了錯(cuò)誤,印證了線程自己分離之后主線程再進(jìn)行join。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-763041.html
如果沒(méi)有出現(xiàn)錯(cuò)誤,那么原因是我們無(wú)法判斷那個(gè)執(zhí)行流先運(yùn)行,如果是主線程先運(yùn)行,在新線程還沒(méi)有分離之前,主線程就已經(jīng)join處于阻塞狀態(tài),那么新線程進(jìn)行分離之后,主線程就無(wú)法得知,因?yàn)橹骶€程處于阻塞狀態(tài),那么也就不會(huì)發(fā)生錯(cuò)誤,我們可以讓主線程sleep(2),或者在主線程中創(chuàng)建線程之后就直接進(jìn)行分離。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-763041.html
到了這里,關(guān)于【Linux】Linux線程概念和線程控制的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!