線程控制原語

線程是進程中最小的執行單位,是進程中的一個實體,是被系統獨立調度和分派的基本單位,它可與同屬一個進程的其它線程共享進程所擁有的所有資源。一個線程能夠建立和撤消另外一個線程,同一進程中的多個線程之間能夠併發執行。瀏覽器

線程,其實也是輕量級的進程。一個進程,即便咱們沒有主動建立線程,也會有一個默認的主線程(即進程自己)。線程只用複雜代碼如何執行,而進程還須要管理內存和文件系統等。線程與資源分配無關,它屬於某一個進程,並與進程內的其餘線程一塊兒共享進程的資源。每一個進程都有本身一套獨立的資源(數據),供其內的全部線程共享。一個進程內的線程通訊比進程之間的通訊更快速和高效。併發

進程至關於一個項目,而線程就是爲了完成項目需求而創建的一個個開發任務。默認狀況下,能夠創建一個大的任務,就是完成某某功能,而後交給一我的從頭作到尾,這就是主線程。但有時候,你發現任務是能夠拆解的,若是沒有很是大的先後關聯關係,就能夠併發執行。例如實現一個瀏覽器,多個頁面標籤能夠是多個進程,可是一個頁面中可能又包含多個進程。函數

察看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); //銷燬線程屬性所佔用的資源 可是,目前咱們能夠暫時不理會,之後須要使用的時候再深究。

相關文章
相關標籤/搜索