# linux線程解析(1)

linux環(huán)境下的線程有joinableunjoinable兩種狀態(tài),joinable下的線程在完成線程函數(shù)(調(diào)用pthread_exit()或者return函數(shù)后)之后不會(huì)自動(dòng)釋放資源,需要在主線程中調(diào)用pthread_join()函數(shù)進(jìn)行阻塞回收,而unjoinable狀態(tài)的線程在完成子線程函數(shù)之后可以自動(dòng)釋放資源(不需要主線程擦屁股)。創(chuàng)建線程時(shí)可以通過(guò)指定參數(shù)來(lái)定義線程的狀態(tài)(默認(rèn)為joinable),也可以通過(guò)pthread_detach(pthid)函數(shù)來(lái)將子線程的狀態(tài)轉(zhuǎn)換為``unjoinable,由于在主線程中調(diào)用pthread_join()會(huì)使得主線程阻塞(等待當(dāng)前子線程的返回),所以一般情況下會(huì)把線程的狀態(tài)調(diào)為unjoinable`,從最大限度的增大主線程的性能。

在linux下創(chuàng)建和管理線程的方法聲明在pthread.h文件中,下面對(duì)幾個(gè)基本函數(shù)進(jìn)行解析:

pthread_create

該函數(shù)的作用是創(chuàng)建一個(gè)進(jìn)程,其函數(shù)原型為:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)

參數(shù)解析:

  • pthread_t *thread:線程id的地址,線程id由pthread_t來(lái)定義,如pthread_t pthid

  • const pthread_attr_t *attr:該參數(shù)指定線程的狀態(tài),比如可以指定detach狀態(tài),如果為NULL則為選擇默認(rèn)的joinable。

  • void *(*start_routine) (void *):函數(shù)指針,指向子線程的回調(diào)函數(shù),需要注意的是該函數(shù)返回的是一個(gè)空指針,傳入的也是一個(gè)空指針。

  • void *arg:向回調(diào)函數(shù)傳入的參數(shù),注意類型是空指針(傳入地址),如果你傳入的參數(shù)是一個(gè)非空指針,需要在回調(diào)函數(shù)中將空指針強(qiáng)制轉(zhuǎn)換為你想要的類型,因?yàn)樵趥魅雲(yún)?shù)的時(shí)候,其他類型的指針會(huì)退化為空指針。比如你傳入了一個(gè)整型變量a的指針,需要在回調(diào)函數(shù)中有以下代碼int* a = (int*)arg,才可以在回調(diào)函數(shù)中使用整型變量a.這里的參數(shù)盡量傳入一個(gè)自定義指針,原因在最后一節(jié)詳解。

pthread_detach

該函數(shù)的功能是將子線程detach(分離),將線程的狀態(tài)改為unjoinable,其函數(shù)聲明為int pthread_detach(pthread_t thread),傳入的參數(shù)是線程id。

pthread_join

該函數(shù)一般在主線程中調(diào)用,用來(lái)將主線程阻塞,以等待當(dāng)前的子線程執(zhí)行完畢(主線程速度比子線程快,如果不等待可能主線程結(jié)束了,子線程還沒(méi)執(zhí)行完就因此關(guān)閉了),其函數(shù)原型為:

int pthread_join(pthread_t thread, void **retval)

參數(shù)解析:

  • pthread_t thread:等待的線程id

  • void **retval:存儲(chǔ)子線程的返回值,(子線程返回值一般由pthread_exit()返回),如果填入NULL表示不保存返回值。這里需要注意的還是返回值的類型是空指針的指針,如果需要使用該返回值,也需要做強(qiáng)制類型轉(zhuǎn)換。

:我們一般在編寫多線程的程序比如多線程的服務(wù)器時(shí),一般不會(huì)使用pthread_join這個(gè)函數(shù),原因有下:1,調(diào)用該函數(shù)之后,主線程會(huì)在該函數(shù)的調(diào)用處進(jìn)行阻塞,等待子線程執(zhí)行完畢才能繼續(xù)向下執(zhí)行,這樣就會(huì)大大拖慢主線程的效率,2,由于主線程使用循環(huán)創(chuàng)建多線程,如果使用了該函數(shù),主線程只有再一個(gè)線程執(zhí)行完成之后才會(huì)再去創(chuàng)建一個(gè)線程,也就是每一個(gè)線程是等前一個(gè)線程執(zhí)行完之后在創(chuàng)建再去執(zhí)行,這樣的多線程顯然效率不高。 一般情況之下,我們會(huì)調(diào)用pthread_detach函數(shù),使得子線程分離,狀態(tài)變?yōu)?code>unjoinable,這樣每一個(gè)線程執(zhí)行完之后自動(dòng)釋放內(nèi)存,主線程也就不需要調(diào)用pthread_join函數(shù)進(jìn)行阻塞,而是可以去循環(huán)創(chuàng)建多個(gè)子線程,提高程序效率。

pthread_create擴(kuò)展

我們?cè)趯懸粋€(gè)多線程程序時(shí)比如多線程的服務(wù)器,會(huì)使用循環(huán)創(chuàng)建多線程,而且我們知道主線程的運(yùn)行速度一般快于子線程,所以在創(chuàng)建線程時(shí)就存在一個(gè)問(wèn)題,那就是我們應(yīng)該如何傳參,如果傳入一個(gè)變量的地址,有可能子線程在沒(méi)有利用該變量執(zhí)行完程序時(shí)變量的值就被主線程修改了,所以我們應(yīng)該傳入一個(gè)自定義指針,這樣相當(dāng)于為每一個(gè)子線程的參數(shù)單獨(dú)創(chuàng)建了一塊地址,不會(huì)被主線程修改,之前的情況相當(dāng)于多個(gè)子線程的參數(shù)共享一塊地址,下面看例子:

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void* print(void* arg){
 sleep(1);
 int* i = (int *)arg;
 std::cout <<i<<"  "<< *i<< std::endl;  //打印變量地址和值
}


int main(int argc, const char** argv) {


 int i = 0;
 while (i<30)
 {

 int a = i;          //是使變量a等于i
 pthread_t pthid;
 pthread_create(&pthid,NULL,print,&a);//創(chuàng)建線程
 pthread_detach(pthid); //改變線程狀態(tài)
 ++i;

 }

 sleep(3);
 return 0;

}

上述程序中主線程循環(huán)創(chuàng)建子線程并將循環(huán)累加的變量i的值賦給子線程進(jìn)行打印結(jié)果過(guò)為:

image-20210418074849005.png

我們發(fā)現(xiàn),所有打印的所有變量的值均為30(16進(jìn)制,我也不知咋回事),這是因?yàn)樵谧泳€程中我們先睡眠了1秒鐘,這期間主線程的循環(huán)早已經(jīng)完成,將i的值累加到了30,看上面的輸出結(jié)果中所有的地址都是一樣的,都是主線程中定義的a的地址。

如果我們每一次循環(huán)都定義一個(gè)指針a,情況會(huì)怎么樣呢,先看代碼:

void* print(void* arg){
 sleep(1);
 int* i = (int *)arg;
 std::cout <<i<<"  "<< *i<< std::endl;  //打印變量地址和值
}


int main(int argc, const char** argv) {


 int i = 0;
 while (i<30)
 {

 int* a = new int(i);       //自定義指針 
 pthread_t pthid;
 pthread_create(&pthid,NULL,print,a);//創(chuàng)建線程
 pthread_detach(pthid); //改變線程狀態(tài)
 ++i;

 }

 sleep(3);
 return 0;

}

:一定在堆上分配空間(即使用new分配空間,返回一個(gè)指針),千萬(wàn)不要使用int *a = i這種在棧上分配空間的方法,因?yàn)?code>int *a = i指明a就是定義一個(gè)指向某一變量的指針,在每次循環(huán)中它指向的地址不會(huì)發(fā)生改變(與前面的實(shí)驗(yàn)結(jié)果一致),使用int* a = new int(i)每一次循環(huán)都會(huì)開(kāi)辟一個(gè)整型空間并返回一個(gè)指針,所以每次循環(huán)中a指向的空間不一樣的,看實(shí)驗(yàn)結(jié)果:

image-20210418080240164.png

這個(gè)圖沒(méi)有截完整,其實(shí)1-30每一個(gè)數(shù)字都有的,不過(guò)影響不大,因?yàn)槲覀兛梢院芮逦目闯雒恳粋€(gè)參數(shù)的地址都不一樣,故而輸出的值都各不相同(1-30)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開(kāi)了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    余生動(dòng)聽(tīng)閱讀 10,869評(píng)論 0 11
  • 彩排完,天已黑
    劉凱書法閱讀 4,482評(píng)論 1 3
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來(lái)的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過(guò)就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,696評(píng)論 2 7

友情鏈接更多精彩內(nèi)容