linux——线程(1)
生活随笔
收集整理的這篇文章主要介紹了
linux——线程(1)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 1.線程概念
- 1.1 什么是線程
- 1.2 線程和進程區別
- 1.3 線程實現原理
- 1.4 三級映射
- 1.5 線程共享資源
- 1.6 線程非共享資源
- 1.7 線程優、缺點
- 2.線程控制原語
- 2.1 pthread_self 函數
- 2.2 pthread_create 函數
- 3.線程與共享
- 3.1 線程共享全局變量
- 4.線程退出
- 4.1 pthread_exit 函數
1.線程概念
1.1 什么是線程
線程:LWP(light weight process), 輕量級的進程,本質仍是進程(在 Linux 環境下)。 線程是進程的子任務,實現進程內部的并發,線程也是CPU調度和執行的最小單位。1.2 線程和進程區別
進程擁有獨立的4G內存地址空間,擁有PCB;線程有獨立的 PCB,但沒有獨立的地址空間(共享)。 進程是最小分配資源單位;線程是最小的執行單位。 進程通信方式:管道、系統IPC(消息隊列、信號量、信號、共享內存)、套接字;線程通信方式:臨界區、互斥量、信號量、事件(信號)。1.3 線程實現原理
但線程不同,兩個線程具有各自獨立的 PCB,但共享同一個頁目錄,也就共享同一個頁表和物理頁面。所以兩個 PCB 共享一個地址空間。實際上,無論是創建進程的 fork,還是創建線程的 pthread_create,底層實現都是調用同一個內核函數 clone。
如果復制對方的地址空間,那么就產出一個“進程”;如果共享對方的地址空間,就產生一個“線程”。
1.4 三級映射
1.5 線程共享資源
1.文件描述符表,在同一個進程內,a線程和b線程共享文件描述符 2.每種信號的處理方式,線程和信號組合復雜,最好不要把它們攪合在一起 3.當前工作目錄 4.用戶 ID 和組 ID 5.內存地址空間 (.text/.data/.bss/heap/共享庫),除了棧空間1.6 線程非共享資源
1.線程 id 2.處理器現場和棧指針(內核棧) 3.獨立的棧空間(用戶空間棧) 4.errno 變量,很特殊的變量,是全局變量,在數據.data段,但是共享 5.信號屏蔽字 6.調度優先級1.7 線程優、缺點
優點: 1. 提高程序并發性 2. 開銷小 3. 數據通信、共享數據方便 缺點: 1. 庫函數,不穩定 2. 調試、編寫困難、gdb 不支持 3. 對信號支持不好 優點相對突出,缺點均不是硬傷。Linux 下由于實現方法導致進程、線程差別不是很大。2.線程控制原語
線程所有操作函數 pthread_* 是庫函數,而非系統調用, 對線程相關函數gcc編譯時,需要鏈接第三方庫-pthread2.1 pthread_self 函數
獲取線程 ID。其作用對應進程中 getpid() 函數。 pthread_t pthread_self(void);返回值:成功:0; 失敗:無!線程 ID:pthread_t 類型,本質:在 Linux 下為無符號整數(%lu),其他系統中可能是結構體實現 線程 ID 是進程內部,識別標志。(兩個進程間,線程 ID 允許相同)注意:不應使用全局變量 pthread_t tid,在子線程中通過 pthread_create 傳出參數來獲取線程 ID, 而應使用pthread_self。2.2 pthread_create 函數
創建一個新線程。 其作用,對應進程中 fork() 函數。 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 返回值:成功:0; 失敗:錯誤號 -----Linux 環境下,所有線程特點,失敗均直接返回錯誤號。 參數: pthread_t:當前 Linux 中可理解為:typedef unsigned long int pthread_t; 參數 1:傳出參數,保存系統為我們分配好的線程 ID 參數 2:通常傳 NULL,表示使用線程默認屬性。若想使用具體屬性也可以修改該參數。 參數 3:函數指針,指向線程主函數(線程體),該函數運行結束,則線程結束。 參數 4:線程主函數執行期間所使用的參數。 在一個線程中調用 pthread_create()創建新的線程后,當前線程從 pthread_create()返回繼續往下執行, 而新的線程所執行的代碼由我們傳給 pthread_create 的函數指針 start_routine 決定。 start_routine 函數接收一個參數,是通過pthread_create 的 arg 參數傳遞給它的, 該參數的類型為 void *,這個指針按什么類型解釋由調用者自己定義。start_routine 的返回值類型也是 void *,這個指針的含義同樣由調用者自己定義。start_routine 返回時,這個線程就退出了, 其它線程可以調用 pthread_join 得到 start_routine 的返回值, 類似于父進程調用 wait(2)得到子進程的退出狀態,稍后詳細介紹 pthread_join。 pthread_create 成功返回后,新創建的線程的 id 被填寫到 thread 參數所指向的內存單元。 我們知道進程 id 的類型是 pid_t,每個進程的 id 在整個系統中是唯一的, 調用 getpid(2)可以獲得當前進程的 id,是一個正整數值。線程id 的類型是 thread_t,它只在當前進程中保證是唯一的,在不同的系統中 thread_t 這個類型有不同的實現, 它可能是一個整數值,也可能是一個結構體,也可能是一個地址, 所以不能簡單地當成整數用 printf 打印,調用 pthread_self(3)可以獲得當前線程的 id。attr 參數表示線程屬性,本節不深入討論線程屬性,所有代碼例子都傳 NULL 給 attr 參數, 表示線程屬性取缺省值,感興趣的讀者可以參考 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線程間共享全局變量!
【牢記】:線程默認共享數據段、代碼段等地址空間,常用的是全局變量。而進程不共享全局變量,只能借助 mmap。
4.線程退出
4.1 pthread_exit 函數
將單個線程退出
void pthread_exit(void *retval); 參數:retval 表示線程退出狀態,通常傳 NULL 思考:使用 exit 將指定線程退出,可以嗎? 【pthrd_exit.c】 結論:線程中,禁止使用 exit 函數,會導致進程內所有線程全部退出。在不添加 sleep 控制輸出順序的情況下。pthread_create 在循環中,幾乎瞬間創建 5 個線程,但只有第 1 個線程有機會輸出(或者第 2 個也有,也可能沒有,取決于內核調度)如果第 3 個線程執行了 exit,將整個進程退出了,所以全部線程退出了。
所以,多線程環境中,應盡量少用,或者不使用 exit 函數,取而代之使用 pthread_exit 函數,將單個線程退出。
任何線程里 exit 導致進程退出,其他線程未工作結束,主控線程退出時不能 return 或 exit。
另注意,pthread_exit 或者 return 返回的指針所指向的內存單元必須是全局的或者是用 malloc 分配的,不能在線程函數的棧上分配,因為當其它線程得到這個返回指針時線程函數已經退出了。
#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主函數中沒有使用return,并且不能使用exit函數,且其等待其它線程結束進程才結束。
總結
以上是生活随笔為你收集整理的linux——线程(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ReaderMe 1.0.0.32版发布
- 下一篇: Linux的JVM可以从SUN网站上下载