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

Linux之線程的創(chuàng)建方式

 更新時間:2023年03月23日 10:21:12   作者:Dutkig  
這篇文章主要介紹了Linux之線程的創(chuàng)建方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

線程的概念與實現(xiàn)方式

線程是進程內(nèi)部的一條執(zhí)行序列或執(zhí)行路徑,一個進程可以包含多條線程。

  • 從資源分配的角度來看,進程是操作系統(tǒng)進行資源分配的基本單位。
  • 從資源調(diào)度的角度來看,線程是資源調(diào)度的最小單位,是程序執(zhí)行的最小單位

執(zhí)行序列就是一組有序指令的集合——函數(shù)。

線程是進程內(nèi)部的一條執(zhí)行序列,一個進程至少有一條線程,稱之為主線程(main方法代表的執(zhí)行序列),可以通過線程庫創(chuàng)建其他線程(給線程制定一個它要執(zhí)行的函數(shù)),將創(chuàng)建的線程稱之為函數(shù)線程。

線程的實現(xiàn)方式

  • 內(nèi)核級線程(由內(nèi)核直接創(chuàng)建和管理線程,雖然創(chuàng)建開銷較大,但是可以利用多處理器的資源)
  • 用戶級線程(由線程庫創(chuàng)建和管理多個線程,線程的實現(xiàn)都是在用戶態(tài),內(nèi)核無法感知,創(chuàng)建開銷較小,無法使用多處理器的資源)
  • 混合級線程(結(jié)合以上兩種方式實現(xiàn),可以利用多處理器的資源,從而在用戶空間中創(chuàng)建更多的線程,從而映射到內(nèi)核空間的線程中,多對多,N:M(N>>M))

Linux系統(tǒng)實現(xiàn)多線程的方式

Linux 實現(xiàn)線程的機制非常獨特。從內(nèi)核的角度來說,它并沒有線程這個概念。

Linux 把所有的線程都當(dāng)做進程來實現(xiàn)。內(nèi)核并沒有準(zhǔn)備特別的調(diào)度算法或是定義特別的數(shù)據(jù)結(jié)構(gòu)來表征線程。

相反,線程僅僅被視為一個與其他進程共享某些資源的進程。

每個線程都擁有唯 一隸屬于自己的task_struct,所以在內(nèi)核中,它看起來就像是一個普通的進程(只是線程和 其他一些進程共享某些資源,如地址空間)

線程和進程的區(qū)別

  • 進程是資源分配最小單位,線程是程序執(zhí)行的最小單位;
  • 線程間的切換效率相比進程間的切換要高
  • 進程有自己獨立的地址空間,每啟動一個進程,系統(tǒng)都會為其分配地址空間,建立數(shù)據(jù)表來維護代碼段、堆棧段和數(shù)據(jù)段,線程沒有獨立的地址空間,它使用相同的地址空間共享數(shù)據(jù);
  • 創(chuàng)建一個線程比進程開銷??;
  • 線程占用的資源要?進程少很多。
  • 線程之間通信更方便,同一個進程下,線程共享全局變量,靜態(tài)變量等數(shù)據(jù),進程之間的通信需要以通信的方式(IPC)進行;(但多線程程序處理好同步與互斥是個難點)
  • 多進程程序更安全,生命力更強,一個進程死掉不會對另一個進程造成影響(源于有獨立的地址空間),多線程程序更不易維護,一個線程死掉,整個進程就死掉了(因為共享地址空間);
  • 進程對資源保護要求高,開銷大,效率相對較低,線程資源保護要求不高,但開銷小,效率高,可頻繁切換;

多線程開發(fā)的三個基本概念

  • 線程 【創(chuàng)建、退出、等待】
  • 互斥鎖【創(chuàng)建、銷毀、加鎖】、解鎖】
  • 條件【創(chuàng)建、銷毀、觸發(fā)、廣播、等待】

線程庫的使用

1.創(chuàng)建線程

#include<phread.h>

int pthread_create(pthread_t *id , pthread_attr_t *attr, void(*fun)(void*), void *arg);
  • id :傳遞一個pthread_t類型的變量的地址,創(chuàng)建成功后,用來獲取新創(chuàng)建的線程的TID
  • attr:指定線程的屬性 默認(rèn)使用NULL
  • fun:線程函數(shù)的地址
  • arg:傳遞給線程函數(shù)的參數(shù)
  • 返回值,成功返回0,失敗返回錯誤碼

多線程代碼示例

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

#include<pthread.h>

//聲明一個線程函數(shù)
void *fun(void *);

int main()
{
	printf("main start\n");

	pthread_t id;
	//創(chuàng)建函數(shù)線程,并且指定函數(shù)線程要執(zhí)行的函數(shù)
	int res = pthread_create(&id,NULL,fun,NULL);
	assert(res == 0);

	//之后并發(fā)運行
	int i = 0;	
	for(; i < 5; i++)
	{
		printf("main running\n");
		sleep(1);
	}

	printf("main over\n");
	exit(0);
}

//定義線程函數(shù)
void* fun(void *arg)
{
	printf("fun start\n");

	int i = 0;
	for(; i < 3;i++)
	{
		printf("fun running\n");
		sleep(1);
	}

	printf("fun over\n");
}

gcc編譯代碼時報`undifined reference to xxxxx錯誤,都是因為程序中調(diào)用了一些方法,但是沒有連接該方法所在的文件,例如下面的情況:

連接庫文件編譯成功并執(zhí)行,這一點在幫助手冊中也有提示:Compile and link with -pthread

比較兩次運行的結(jié)果發(fā)現(xiàn)前三條執(zhí)行語句時一樣的

結(jié)論

  • 創(chuàng)建線程并執(zhí)行線程函數(shù),和調(diào)用函數(shù)是完全不同的概念。
  • 主線程和函數(shù)線程是并發(fā)執(zhí)行的。
  • 線程提前于主線程結(jié)束時,不會影響主線程的運行
  • 主線程提前于線程結(jié)束時,整個進程都會結(jié)束,其他線程也會結(jié)束
  • 創(chuàng)建函數(shù)線程后,哪個線程先被執(zhí)行是有操作系統(tǒng)的調(diào)度算法和機器環(huán)境決定。

函數(shù)線程在主線程結(jié)束后也隨之退出,原因:主線程結(jié)束時使用的是exit方法,這個方法結(jié)束的是進程。

然而修改代碼為:pthread_exit(NULL);此時主線程結(jié)束,函數(shù)線程會繼續(xù)執(zhí)行直至完成。即便如此,我們還是不推薦大家手動結(jié)束主線程,我們更喜歡讓主線程等待一會。

給線程函數(shù)傳參

①值傳遞

將變量的值直接轉(zhuǎn)成void*類型進行傳遞

因為線程函數(shù)接受的是一個void*類型的指針,只要是指針,32位系統(tǒng)上都是4個字節(jié),值傳遞就只能傳遞小于或等于4字節(jié)的值。

代碼示例

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

#include<pthread.h>

void *fun(void *);

int main()
{
	printf("main start\n");

	int a = 10;
	
	pthread_t id;
	int res = pthread_create(&id,NULL,fun,(void*)a);
	assert(res == 0);

	int i = 0;	
	for(; i < 5; i++)
	{
		printf("main running\n");
		sleep(1);
	}

	printf("main over\n");
	exit(0);
}


void* fun(void *arg)
{
	int b = (int)arg;
	printf("b == %d\n",b);
}

②地址傳遞

將變量(所有類型)的地址強轉(zhuǎn)成void*類型進行傳遞,就和在普通函數(shù)調(diào)用傳遞變量的地址相似。

主線程和函數(shù)線程通過這個地址就可以共享地址所指向的空間。

一個進程內(nèi)的所有線程是共享這個進程的地址空間。

多線程下進程的4G虛擬地址空間

一個進程內(nèi)的所有線程對于全局?jǐn)?shù)據(jù),靜態(tài)數(shù)據(jù),堆區(qū)空間都是共享的。

線程之間傳遞數(shù)據(jù)很簡單,但是隨之帶來的問題就是線程并發(fā)運行時無法保證線程安全。

代碼示例

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

#include<pthread.h>

int gdata = 10; //.data

void *fun(void *);

int main()
{
	int *ptr = (int *)malloc(4);//.heap
    *ptr = 10;
	
	pthread_t id;
	int res = pthread_create(&id,NULL,fun,(void*)ptr);
	assert(res == 0);

    sleep(2);//等待兩秒,保證函數(shù)線程已經(jīng)講數(shù)據(jù)修改

	printf("main : gdata == %d\n",gdata);
    printf("main : *ptr = %d\n",*ptr);

	exit(0);
}


void *fun(void *arg)
{
	int *p = (int*)arg;

    gdata = 20000;
    *p = 20;

	printf("fun over\n");
}

線程庫中的其他方法

線程退出的三種方式:

  • 線程從執(zhí)行函數(shù)返回,返回值是線程的退出碼;
  • 線程被同一進程的其他線程取消;
  • 調(diào)用pthread_exit()函數(shù)退出;

等待線程終止

int pthread_join(pthread_t thread, void **retval);
args:
    pthread_t thread: 被連接線程的線程號,該線程必須位于當(dāng)前進程中,而且不得是分離線程
    void **retval :該參數(shù)不為NULL時,指向某個位置 在該函數(shù)返回時,將該位置設(shè)置為已終止線程的退出狀態(tài)
    return:
    線程連接的狀態(tài),0是成功,非0是失敗

當(dāng)A線程調(diào)用線程B并 pthread_join() 時,A線程會處于阻塞狀態(tài),直到B線程結(jié)束后,A線程才會繼續(xù)執(zhí)行下去。當(dāng) pthread_join() 函數(shù)返回后,被調(diào)用線程才算真正意義上的結(jié)束,它的內(nèi)存空間也會被釋放(如果被調(diào)用線程是非分離的)。

這里有三點需要注意:

  • 1.被釋放的內(nèi)存空間僅僅是系統(tǒng)空間,你必須手動清除程序分配的空間,比如 malloc() 分配的空間。
  • 2.一個線程只能被一個線程所連接。
  • 3.被連接的線程必須是非分離的,否則連接會出錯。所以可以看出pthread_join()有兩種作用:1-用于等待其他線程結(jié)束:當(dāng)調(diào)用 pthread_join() 時,當(dāng)前線程會處于阻塞狀態(tài),直到被調(diào)用的線程結(jié)束后,當(dāng)前線程才會重新開始執(zhí)行。2-對線程的資源進行回收:如果一個線程是非分離的(默認(rèn)情況下創(chuàng)建的線程都是非分離)并且沒有對該線程使用 pthread_join() 的話,該線程結(jié)束后并不會釋放其內(nèi)存空間,這會導(dǎo)致該線程變成了“僵尸線程”。

等待指定的子線程結(jié)束

  • 等待thread()指定的線程退出,線程未退出時,該方法阻塞
  • result接收thread線程退出時,指定退出信息
int pthread_join(pthread_t id,void **result)//調(diào)用這個方法的線程會阻塞,直到等待線程結(jié)束

代碼演示:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

#include<pthread.h>

int main()
{
	printf("main start\n");

	pthread_t id;
	int res = pthread_create(&id,NULL,fun,NULL);
	assert(res == 0);

	//之后并發(fā)運行
	int i = 0;	
	for(; i < 5; i++)
	{
		printf("main running\n");
		sleep(1);
	}
	
	char *s = NULL;
	pthread_join(id,(void **)&s);
	printf("join : s = %s\n",s);
	
	exit(0);
}

//定義線程函數(shù)
void* fun(void *arg)
{
	printf("fun start\n");

	int i = 0;
	for(; i < 10;i++)
	{
		printf("fun running\n");
		sleep(1);
	}

	printf("fun over\n");

	pthread_exit("fun over");//將該字符常量返回給主線程
}

此時,主線程完成五次輸出,就會等待子線程結(jié)束,阻塞等待,子線程結(jié)束后,最后,主線程打印join:s = fun over

關(guān)于exit和join的一些詳細說明:

  • 線程自己運行結(jié)束,或者調(diào)用pthread_exit結(jié)束,線程都會釋放自己獨有的空間資源;
  • 若線程是非分離的,線程會保留線程ID號,直到其他線程通過joining這個線程確認(rèn)其已經(jīng)死亡,join的結(jié)果是joining線程得到已終止線程的退出狀態(tài),已終止線程將消失;
  • 若線程是分離的,不需要使用pthread_exit(),線程自己運行結(jié)束,線程結(jié)束就會自己釋放所有空間資源(包括線程ID號);
  • 子線程最終一定要使用pthread_join()或者設(shè)置為分離線程來結(jié)束線程,否則線程的資源不會被完全釋放(使用取消線程功能也不能完全釋放);
  • 主線程運行pthrea_exit(),會結(jié)束主線程,但是不會結(jié)束子線程;
  • 主線程結(jié)束,則整個程序結(jié)束,所以主線程最好使用pthread_join函數(shù)等待子線程結(jié)束,使用該函數(shù)一個線程可以等待多個線程結(jié)束;
  • 使用pthread_join函數(shù)的線程將會阻塞,直到被join的函數(shù)線程結(jié)束,該函數(shù)返回,但是它對被等待終止的線程運行沒有影響;
  • 如果子線程使用exit()則可以結(jié)束整個進程;

線程屬性

線程具有的屬性可以在線程創(chuàng)建的時候指定;

——pthread_create()函數(shù)的第二個參數(shù)(pthread_attr_t *attr)表示線程的屬性,在以前的例子中將其值設(shè)為NULL,也就是采用默認(rèn)屬性,線程的多項屬性都是可以修改的,這些屬性包括綁定屬性,分離屬性,堆棧屬性,堆棧大小,優(yōu)先級。

系統(tǒng)默認(rèn)的是非綁定,非分離,缺省1M的堆棧以及父子進程優(yōu)先級相同

線程結(jié)構(gòu)如下:

typedef struct
{
    int             detachstate;     //線程的分離狀態(tài)
    int             schedpolicy;    //線程調(diào)度策略
    struct sched_param  schedparam; //線程的調(diào)度參數(shù)
    int             inheritsched;   //線程的繼承性
    int             scope;      //線程的作用域
    size_t          guardsize;  //線程棧末尾的警戒緩沖區(qū)大小
    int             stackaddr_set; //線程的棧設(shè)置
    void*           stackaddr;  //線程棧的位置
    size_t          stacksize;  //線程棧的大小
} pthread_attr_t;

每一個屬性都有對應(yīng)的一些函數(shù),用于對其進行查看和修改,下面分別介紹:

線程屬性初始化

初始化和去初始化分別對應(yīng)于如下的兩個函數(shù):

#include <pthread.h>
?
①int pthread_attr_init(pthread_attr_t *attr);
②it pthread_attr_destroy(pthread_attr_t *attr);

①功能:

  • 初始化線程屬性函數(shù),注意:應(yīng)先初始化線程屬性,再pthread_create創(chuàng)建線程

參數(shù):

  • attr:線程屬性結(jié)構(gòu)體

返回值:

  • 成功:0
  • 失?。?1

②?功能:

  • 銷毀線程屬性所占用的資源函數(shù)

參數(shù):

  • attr:線程屬性結(jié)構(gòu)體

返回值:

  • 成功:0
  • 失?。?1

線程分離

線程的分離狀態(tài)決定一個線程以什么樣的方式來終止自己,這個在之前我們也說過了。

  • 非分離狀態(tài):線程的默認(rèn)屬性是非分離狀態(tài),這種情況下,原有的線程等待創(chuàng)建的線程結(jié)束。只有當(dāng)pthread_join()函數(shù)返回時,創(chuàng)建的線程才算終止,才能釋放自己占用的系統(tǒng)資源。
  • 分離狀態(tài):分離線程沒有被其他的線程所等待,自己運行結(jié)束了,線程也就終止了,馬上釋放系統(tǒng)資源。應(yīng)該根據(jù)自己的需要,選擇適當(dāng)?shù)姆蛛x狀態(tài)。

相關(guān)API如下:

#include <pthread.h>
?
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

功能:設(shè)置線程分離狀態(tài)

參數(shù):

  • attr:已初始化的線程屬性
  • detachstate: 分離狀態(tài)

PTHREAD_CREATE_DETACHED(分離線程)

PTHREAD_CREATE_JOINABLE(非分離線程)

返回值:

  • 成功:0
  • 失敗:非0

?

int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);

功能:獲取線程分離狀態(tài)

參數(shù):

  • attr:已初始化的線程屬性detachstate: 分離狀態(tài)

PTHREAD_CREATE_DETACHED(分離線程)

PTHREAD _CREATE_JOINABLE(非分離線程)

返回值:

  • 成功:0
  • 失敗:非0

注意:

當(dāng)一個線程被設(shè)置為分離線程時,假設(shè)此時該線程的執(zhí)行速度非???,它很可能在pthread_create返回之前就終止; 終止之后將線程號和系統(tǒng)資源移交給其他線程使用,這樣調(diào)用create就得到了錯誤的線程號,因此就必須采取一些同步措施,可以在被創(chuàng)建的線程里調(diào)用pthread_cond_timedwait函數(shù),讓這個線程等待一會兒,留出足夠的時間讓函數(shù)pthread_create返回,設(shè)置一段等待時間,是在多線程編程里常用的方法。但是注意不要使用諸如wait()之類的函數(shù),它們是使整個進程睡眠,并不能解決線程同步的問題。

總結(jié)

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

相關(guān)文章

最新評論

舒兰市| 武陟县| 鄂伦春自治旗| 资兴市| 宾阳县| 合江县| 安平县| 厦门市| 连城县| 延寿县| 开远市| 微山县| 收藏| 新宁县| 忻城县| 平果县| 云霄县| 朔州市| 南漳县| 宁德市| 利津县| 裕民县| 米泉市| 祁阳县| 韶关市| 铜梁县| 河东区| 普兰店市| 琼中| 饶平县| 连云港市| 海伦市| 南宁市| 西安市| 景宁| 恩施市| 六枝特区| 嘉祥县| 内乡县| 若尔盖县| 元氏县|