線程是進程中最小的執行單位,是進程中的一個實體,是被系統獨立調度和分派的基本單位,它可與同屬一個進程的其它線程共享進程所擁有的所有資源。一個線程能夠建立和撤消另外一個線程,同一進程中的多個線程之間能夠併發執行。瀏覽器
線程,其實也是輕量級的進程。一個進程,即便咱們沒有主動建立線程,也會有一個默認的主線程(即進程自己)。線程只用複雜代碼如何執行,而進程還須要管理內存和文件系統等。線程與資源分配無關,它屬於某一個進程,並與進程內的其餘線程一塊兒共享進程的資源。每一個進程都有本身一套獨立的資源(數據),供其內的全部線程共享。一個進程內的線程通訊比進程之間的通訊更快速和高效。併發
進程至關於一個項目,而線程就是爲了完成項目需求而創建的一個個開發任務。默認狀況下,能夠創建一個大的任務,就是完成某某功能,而後交給一我的從頭作到尾,這就是主線程。但有時候,你發現任務是能夠拆解的,若是沒有很是大的先後關聯關係,就能夠併發執行。例如實現一個瀏覽器,多個頁面標籤能夠是多個進程,可是一個頁面中可能又包含多個進程。函數
察看LWP(light-weight process,輕量級進程)號(線程號):spa
線程間的共享資源
1.文件描述符表
2.每種信號的處理方式
3.當前工做目錄
4.用戶ID和組ID
5.內存地址空間線程
線程間的非共享資源
1.線程id
2.處理器現場和棧指針(內核棧)
3.獨立的棧空間(用戶空間棧)
4.errno變量
5.信號屏蔽字
6.調度優先級3d
線程的優勢:unix
提升程序的併發性
開銷小,不用從新分配內存
通訊和共享數據方便指針
線程的缺點:調試
線程不穩定(庫函數實現,因此在編譯時須要連接線程庫即 -lpthread)
線程調試比較困難(gdb支持很差)
線程沒法使用unix經典事件,例如信號code
查看manpage關於pthread的函數:man -k pthread
建立線程:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
pthread_t *thread:傳遞一個pthread_t變量地址進來,用於保存新線程的tid(線程ID)
const pthread_attr_t *attr:線程屬性設置,如使用默認屬性,則傳NULL
void *(*start_routine) (void *):函數指針,指向新線程應該加載執行的函數模塊
void *arg:指定線程將要加載調用的那個函數的參數返回值:成功返回0,失敗返回錯誤號。之前學過的系統函數都是成功返回0,失敗返回-1,而錯誤號保存在全局變量errno中,而pthread庫的函數都是經過返回值返回錯誤號,雖然每一個線程也都有一個errno,但這是爲了兼容其它函數接口而提供的,pthread庫自己並不使用它,經過返回值返回錯誤碼更加清晰。
Compile and link with -lpthread.
typedef unsigned long int pthread_t;
在一個線程中調用pthread_create()建立新的線程後,當前線程從pthread_create()返回繼續往下執行,而新的線程所執行的代碼由咱們傳給pthread_create的函數指針start_routine決定。start_routine函數接收一個參數,是經過pthread_create的arg參數傳遞給它的,該參數的類型爲void *,這個指針按什麼類型解釋由調用者本身定義。start_routine的返回值類型也是void *,這個指針的含義一樣由調用者本身定義。start_routine返回時,這個線程就退出了,其它線程能夠調用pthread_join獲得start_routine的返回值。
pthread_create成功返回後,新建立的線程的id被填寫到thread參數所指向的內存單元。咱們知道進程id的類型是pid_t,每一個進程的id在整個系統中是惟一的,調用getpid()能夠得到當前進程的id,是一個正整數值。線程id的類型是thread_t,它只在當前進程中保證是惟一的,在不一樣的系統中thread_t這個類型有不一樣的實現,它多是一個整數值,也多是一個結構體,也多是一個地址,因此不能簡單地當成整數用printf打印,調用pthread_self()能夠得到當前線程的id。
獲取調用線程tid:
pthread_t pthread_self(void);
退出線程pthread_exit:
void pthread_exit(void *retval);
void *retval:線程退出時傳遞出的參數,能夠是退出值或地址,如是地址時,不能是線程內部申請的局部地址。
調用線程退出函數,注意和exit函數的區別,任何線程裏調用exit函數都會致使進程退出。其餘線程未工做結束,主控線程退出時不能return或exit。若是主線程建立了線程以後,當即使用return返回了,此時子線程可能沒法執行,可是主線程若是使用pthread_exit退出,子線程則能夠執行。pthread_exit是退出當前線程,不會影響其餘線程的運行。
須要注意,pthread_exit或者return返回的指針所指向的內存單元必須是全局的或者靜態的或者是用malloc分配的,不能在線程函數的棧上分配,由於當其它線程獲得這個返回指針時線程函數已經退出了。
回收線程:
int pthread_join(pthread_t thread, void **retval);
pthread_t thread:回收線程的tid
void **retval:接收退出線程傳遞出的返回值
返回值:成功返回0,失敗返回錯誤號
調用該函數的線程將掛起等待,直到id爲thread的線程終止。thread線程以不一樣的方法終止,經過pthread_join獲得的終止狀態是不一樣的,總結以下:
若是thread線程經過return返回,retval所指向的單元裏存放的是thread線程函數的返回值。若是thread線程被別的線程調用pthread_cancel異常終止掉,retval所指向的單元裏存放的是常數PTHREAD_CANCELED。若是thread線程是本身調用pthread_exit終止的,retval所指向的單元存放的是傳給pthread_exit的參數。若是對thread線程的終止狀態不感興趣,能夠傳NULL給retval參數。
在進程內某個線程能夠取消另外一個線程。
int pthread_cancel(pthread_t thread);
被取消的線程,退出值,定義在Linux的pthread庫中常數PTHREAD_CANCELED的值是-1。能夠在頭文件pthread.h中找到它的定義:
當建立多個線程時,哪個線程最早執行依賴於具體的系統實現。因爲pthread_create的錯誤碼不保存在errno中,所以不能直接用perror()打印錯誤信
息,能夠先用strerror()把錯誤碼轉換成錯誤信息再打印。
若是任意一個線程調用了exit或_exit,則整個進程的全部線程都終止,從main函數return也至關於調用exit。
eg:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 #include <unistd.h> 5 #include <string.h> 6 struct STU{ 7 int age; 8 char name[20]; 9 }; 10 11 void sys_err(int err, void * exitno) 12 { 13 fprintf(stderr,"can't creat thread: %s\n",strerror(err)); 14 pthread_exit(exitno); 15 } 16 void *thr_fn1(void *arg) 17 { 18 pthread_t tid; 19 tid=pthread_self(); 20 printf("thread 1 . ID = %ld =0x%X\n",(unsigned long)tid,(unsigned int)tid); 21 return (void *)1; 22 } 23 void *thr_fn2(void *arg) 24 { 25 26 pthread_t tid; 27 tid=pthread_self(); 28 printf("thread 2 . ID = %ld =0x%X\n",(unsigned long)tid,(unsigned int)tid); 29 ((struct STU *)arg)->age=100; 30 sprintf(((struct STU *)arg)->name,"%s","Lihua"); 31 pthread_exit((void *)2); 32 } 33 void *thr_fn3(void *arg) 34 { 35 pthread_t tid; 36 tid=pthread_self(); 37 while(1) 38 { 39 printf("thread 3 . ID = %ld =0x%X\n",(unsigned long)tid,(unsigned int)tid); 40 sleep(1); 41 } 42 return (void *)3; 43 } 44 45 int main(int argc,char *argv[]) 46 { 47 pthread_t tid,tid1,tid2,tid3; 48 void *tret1,*tret2,*tret3; 49 int err; 50 51 struct STU test; 52 53 tid=pthread_self(); 54 printf("main thread = %ld =0x%X\n",(unsigned long)tid,(unsigned int)tid); 55 err=pthread_create(&tid1,NULL,thr_fn1,NULL); 56 if(err!=0) 57 { 58 sys_err(err,(void *)-1); 59 } 60 err=pthread_create(&tid2,NULL,thr_fn2,&test); 61 if(err!=0) 62 { 63 sys_err(err,(void *)-2); 64 } 65 66 err=pthread_create(&tid3,NULL,thr_fn3,NULL); 67 if(err!=0) 68 { 69 sys_err(err,(void *)-3); 70 } 71 72 pthread_join(tid1,&tret1);//阻塞等待線程1結束 73 printf("thread 1 exit code is %ld\n",(long)tret1); 74 75 pthread_join(tid2,&tret2); 76 printf("thread 2 exit code is %ld\n",(long)tret2); 77 78 printf("arg->age=%d, arg->name=%s\n",test.age,test.name); 79 80 sleep(3); 81 82 pthread_cancel(tid3); 83 pthread_join(tid3,&tret3); 84 printf("thread 3 exit code is %ld\n",(long)tret3);//注意,調用cancel以後,此時線程的退出值不是3而是-1 85 86 pthread_exit((void *)0);//return 0; 87 }
判斷兩個線程ID是否相同:
int pthread_equal(pthread_t t1, pthread_t t2);
分離屬性:
int pthread_detach(pthread_t tid);
pthread_t tid:分離線程tid
返回值:成功返回0,失敗返回錯誤號。
通常狀況下,線程終止後,其終止狀態一直保留到其它線程調用pthread_join獲取
它的狀態爲止。可是線程也能夠被置爲detach狀態,這樣的線程一旦終止就馬上回收
它佔用的全部資源,而不保留終止狀態。不能對一個已經處於detach狀態的線程調用
pthread_join,這樣的調用將返回EINVAL。若是已經對一個線程調用了pthread_detach就不
能再調用pthread_join了 。
最後,還有設置線程屬性的一些相關函數:
int pthread_attr_init(pthread_attr_t *attr); //初始化線程屬性int pthread_attr_destroy(pthread_attr_t *attr); //銷燬線程屬性所佔用的資源 可是,目前咱們能夠暫時不理會,之後須要使用的時候再深究。