linux——线程(1)
文章目錄
- 1.線程概念
- 1.1 什么是線程
- 1.2 線程和進程區(qū)別
- 1.3 線程實現(xiàn)原理
- 1.4 三級映射
- 1.5 線程共享資源
- 1.6 線程非共享資源
- 1.7 線程優(yōu)、缺點
 
- 2.線程控制原語
- 2.1 pthread_self 函數(shù)
- 2.2 pthread_create 函數(shù)
 
- 3.線程與共享
- 3.1 線程共享全局變量
 
- 4.線程退出
- 4.1 pthread_exit 函數(shù)
 
1.線程概念
1.1 什么是線程
線程:LWP(light weight process), 輕量級的進程,本質(zhì)仍是進程(在 Linux 環(huán)境下)。 線程是進程的子任務(wù),實現(xiàn)進程內(nèi)部的并發(fā),線程也是CPU調(diào)度和執(zhí)行的最小單位。1.2 線程和進程區(qū)別
進程擁有獨立的4G內(nèi)存地址空間,擁有PCB;線程有獨立的 PCB,但沒有獨立的地址空間(共享)。 進程是最小分配資源單位;線程是最小的執(zhí)行單位。 進程通信方式:管道、系統(tǒng)IPC(消息隊列、信號量、信號、共享內(nèi)存)、套接字;線程通信方式:臨界區(qū)、互斥量、信號量、事件(信號)。1.3 線程實現(xiàn)原理
但線程不同,兩個線程具有各自獨立的 PCB,但共享同一個頁目錄,也就共享同一個頁表和物理頁面。所以兩個 PCB 共享一個地址空間。實際上,無論是創(chuàng)建進程的 fork,還是創(chuàng)建線程的 pthread_create,底層實現(xiàn)都是調(diào)用同一個內(nèi)核函數(shù) clone。
如果復(fù)制對方的地址空間,那么就產(chǎn)出一個“進程”;如果共享對方的地址空間,就產(chǎn)生一個“線程”。
1.4 三級映射
1.5 線程共享資源
1.文件描述符表,在同一個進程內(nèi),a線程和b線程共享文件描述符 2.每種信號的處理方式,線程和信號組合復(fù)雜,最好不要把它們攪合在一起 3.當(dāng)前工作目錄 4.用戶 ID 和組 ID 5.內(nèi)存地址空間 (.text/.data/.bss/heap/共享庫),除了??臻g1.6 線程非共享資源
1.線程 id 2.處理器現(xiàn)場和棧指針(內(nèi)核棧) 3.獨立的棧空間(用戶空間棧) 4.errno 變量,很特殊的變量,是全局變量,在數(shù)據(jù).data段,但是共享 5.信號屏蔽字 6.調(diào)度優(yōu)先級1.7 線程優(yōu)、缺點
優(yōu)點: 1. 提高程序并發(fā)性 2. 開銷小 3. 數(shù)據(jù)通信、共享數(shù)據(jù)方便 缺點: 1. 庫函數(shù),不穩(wěn)定 2. 調(diào)試、編寫困難、gdb 不支持 3. 對信號支持不好 優(yōu)點相對突出,缺點均不是硬傷。Linux 下由于實現(xiàn)方法導(dǎo)致進程、線程差別不是很大。2.線程控制原語
線程所有操作函數(shù) pthread_* 是庫函數(shù),而非系統(tǒng)調(diào)用, 對線程相關(guān)函數(shù)gcc編譯時,需要鏈接第三方庫-pthread2.1 pthread_self 函數(shù)
獲取線程 ID。其作用對應(yīng)進程中 getpid() 函數(shù)。 pthread_t pthread_self(void);返回值:成功:0; 失敗:無!線程 ID:pthread_t 類型,本質(zhì):在 Linux 下為無符號整數(shù)(%lu),其他系統(tǒng)中可能是結(jié)構(gòu)體實現(xiàn) 線程 ID 是進程內(nèi)部,識別標(biāo)志。(兩個進程間,線程 ID 允許相同)注意:不應(yīng)使用全局變量 pthread_t tid,在子線程中通過 pthread_create 傳出參數(shù)來獲取線程 ID, 而應(yīng)使用pthread_self。2.2 pthread_create 函數(shù)
創(chuàng)建一個新線程。 其作用,對應(yīng)進程中 fork() 函數(shù)。 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 返回值:成功:0; 失敗:錯誤號 -----Linux 環(huán)境下,所有線程特點,失敗均直接返回錯誤號。 參數(shù): pthread_t:當(dāng)前 Linux 中可理解為:typedef unsigned long int pthread_t; 參數(shù) 1:傳出參數(shù),保存系統(tǒng)為我們分配好的線程 ID 參數(shù) 2:通常傳 NULL,表示使用線程默認屬性。若想使用具體屬性也可以修改該參數(shù)。 參數(shù) 3:函數(shù)指針,指向線程主函數(shù)(線程體),該函數(shù)運行結(jié)束,則線程結(jié)束。 參數(shù) 4:線程主函數(shù)執(zhí)行期間所使用的參數(shù)。 在一個線程中調(diào)用 pthread_create()創(chuàng)建新的線程后,當(dāng)前線程從 pthread_create()返回繼續(xù)往下執(zhí)行, 而新的線程所執(zhí)行的代碼由我們傳給 pthread_create 的函數(shù)指針 start_routine 決定。 start_routine 函數(shù)接收一個參數(shù),是通過pthread_create 的 arg 參數(shù)傳遞給它的, 該參數(shù)的類型為 void *,這個指針按什么類型解釋由調(diào)用者自己定義。start_routine 的返回值類型也是 void *,這個指針的含義同樣由調(diào)用者自己定義。start_routine 返回時,這個線程就退出了, 其它線程可以調(diào)用 pthread_join 得到 start_routine 的返回值, 類似于父進程調(diào)用 wait(2)得到子進程的退出狀態(tài),稍后詳細介紹 pthread_join。 pthread_create 成功返回后,新創(chuàng)建的線程的 id 被填寫到 thread 參數(shù)所指向的內(nèi)存單元。 我們知道進程 id 的類型是 pid_t,每個進程的 id 在整個系統(tǒng)中是唯一的, 調(diào)用 getpid(2)可以獲得當(dāng)前進程的 id,是一個正整數(shù)值。線程id 的類型是 thread_t,它只在當(dāng)前進程中保證是唯一的,在不同的系統(tǒng)中 thread_t 這個類型有不同的實現(xiàn), 它可能是一個整數(shù)值,也可能是一個結(jié)構(gòu)體,也可能是一個地址, 所以不能簡單地當(dāng)成整數(shù)用 printf 打印,調(diào)用 pthread_self(3)可以獲得當(dāng)前線程的 id。attr 參數(shù)表示線程屬性,本節(jié)不深入討論線程屬性,所有代碼例子都傳 NULL 給 attr 參數(shù), 表示線程屬性取缺省值,感興趣的讀者可以參考 APUE。 #include<stdio.h> #include<pthread.h> #include <stdlib.h> #include<unistd.h>void* print(void* arg){printf("in print:pthread id=%lu,pid=%u\n",pthread_self(),getpid());return NULL; } int main() {pthread_t tid;//線程IDprintf("in main1:pthread id=%lu,pid=%u\n",pthread_self(),getpid());tid=pthread_create(&tid,NULL,print,NULL);sleep(1);if(tid!=0){printf("pthread_create error\n");exit(1);}printf("in main1:pthread id=%lu,pid=%u\n",pthread_self(),getpid());return 0; } zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ vim pthread_create.c zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ gcc pthread_create.c -o pthread_create -lpthread zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./pthread_create in main1:pthread id=140607852943168,pid=33317 in print:pthread id=140607852939008,pid=33317 in main1:pthread id=140607852943168,pid=33317 #include<stdio.h> #include<pthread.h> #include<unistd.h> #include<stdlib.h> #include<string.h>void* func(void* arg){int i=(int)arg;//sleep(i);printf("第%d個線程,pthread id=%lu,pid=%u\n",i,pthread_self(),getpid());return NULL; } int main() {pthread_t tid;int i,ret;for(int i=0;i<5;i++){ret=pthread_create(&tid,NULL,func,(void*)i);if(ret!=0){fprintf(stderr,"pthread_create error:%s\n",strerror(ret));exit(1);}}sleep(1);printf("main pthread id=%lu,pid=%u\n",pthread_self(),getpid());return 0; } zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./cycle_pthread_create 第0個線程,pthread id=139641383798528,pid=36526 第3個線程,pthread id=139641265059584,pid=36526 第4個線程,pthread id=139641358620416,pid=36526 第1個線程,pthread id=139641375405824,pid=36526 第2個線程,pthread id=139641367013120,pid=36526 main pthread id=139641383802688,pid=365263.線程與共享
3.1 線程共享全局變量
#include<stdio.h> #include<pthread.h> #include<unistd.h> #include<stdlib.h>int a=10;void* func1(void* arg){a=100;printf("pthread id=%lu,a=%d\n",pthread_self(),a);return NULL; }void* func2(void* arg){printf("pthread id=%lu,a=%d\n",pthread_self(),a);return NULL; } int main() {pthread_t tid1,tid2;int ret;ret=pthread_create(&tid1,NULL,func1,NULL);if(ret!=0){printf("pthread create error\n");exit(1);}sleep(1);ret=pthread_create(&tid2,NULL,func2,NULL);if(ret!=0){printf("pthread create error\n");exit(1);}sleep(1);return 0; } zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./pthread_value pthread id=140622421137152,a=100 pthread id=140622412744448,a=100線程間共享全局變量!
 【牢記】:線程默認共享數(shù)據(jù)段、代碼段等地址空間,常用的是全局變量。而進程不共享全局變量,只能借助 mmap。
4.線程退出
4.1 pthread_exit 函數(shù)
將單個線程退出
void pthread_exit(void *retval); 參數(shù):retval 表示線程退出狀態(tài),通常傳 NULL 思考:使用 exit 將指定線程退出,可以嗎? 【pthrd_exit.c】 結(jié)論:線程中,禁止使用 exit 函數(shù),會導(dǎo)致進程內(nèi)所有線程全部退出。在不添加 sleep 控制輸出順序的情況下。pthread_create 在循環(huán)中,幾乎瞬間創(chuàng)建 5 個線程,但只有第 1 個線程有機會輸出(或者第 2 個也有,也可能沒有,取決于內(nèi)核調(diào)度)如果第 3 個線程執(zhí)行了 exit,將整個進程退出了,所以全部線程退出了。
 所以,多線程環(huán)境中,應(yīng)盡量少用,或者不使用 exit 函數(shù),取而代之使用 pthread_exit 函數(shù),將單個線程退出。
 任何線程里 exit 導(dǎo)致進程退出,其他線程未工作結(jié)束,主控線程退出時不能 return 或 exit。
另注意,pthread_exit 或者 return 返回的指針?biāo)赶虻膬?nèi)存單元必須是全局的或者是用 malloc 分配的,不能在線程函數(shù)的棧上分配,因為當(dāng)其它線程得到這個返回指針時線程函數(shù)已經(jīng)退出了。
#include<stdio.h> #include<pthread.h> #include <stdlib.h> #include<unistd.h>void* print(void* arg){sleep(1);printf("in print:pthread id=%lu,pid=%u\n",pthread_self(),getpid());return NULL; } int main() {pthread_t tid;//線程IDint ret;printf("in main1:pthread id=%lu,pid=%u\n",pthread_self(),getpid());ret=pthread_create(&tid,NULL,print,NULL);sleep(1);if(ret!=0){printf("pthread_create error\n");exit(1);}printf("in main1:pthread id=%lu,pid=%u\n",pthread_self(),getpid());pthread_exit(NULL); } zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./pthread_exit in main1:pthread id=139696593151808,pid=36805 in main1:pthread id=139696593151808,pid=36805 in print:pthread id=139696593147648,pid=36805main主函數(shù)中沒有使用return,并且不能使用exit函數(shù),且其等待其它線程結(jié)束進程才結(jié)束。
總結(jié)
以上是生活随笔為你收集整理的linux——线程(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: ReaderMe 1.0.0.32版发布
- 下一篇: 计算机网络学习笔记-1.2.4TCP,I
