線程(上)

一、什麼是線程


定義:
一個程序的一個執行路線叫做線程,更準確的說:“線程是進程內部的控制序列
一個進程至少有一個線程
線程包含了表示進程內執行環境必須的信息,其中包括進程中標識線程的線程ID,一組寄存器值,棧,調度優先級和策略,信號屏蔽字,errno變量及線程私有數據。
進程的所有信息爲該進程的所有線程所共享,包括可執行的程序文本,程序的全局內存和堆內存,棧以及文件描述符
使用ps -elf查看線程

二、進程與線程的區別


a. 進程是資源競爭的最小單位
b. 線程是程序執行,系統調度的最小單位,是輕量級進程
c. 線程共享進程的數據,但同時也擁有自己的一部分數據:線程id,一組寄存器,棧,errno,信號屏蔽字,調度優先級
d. 一個進程中的多個線程共享以下內容:
同一地址空間,文件描述符表,每個信號的處理方式,當前工作目錄,用戶id與組id

三、線程的優缺點


1.線程的優點


a. 創建⼀個新線程的代價要⽐創建⼀個新進程⼩得多
b.與進程之間的切換相⽐,線程之間的切換需要操作系統做的⼯作要少很多
c. 線程佔⽤的資源要⽐進程少很多
d. 能充分利⽤多處理器的可並⾏數量
e. 在等待慢速I/O操作結束的同時,程序可執⾏其他的計算任務
f. 計算密集型應⽤,爲了能在多處理器系統上運⾏,將計算分解到多個線程中實現
g. I/O密集型應⽤,爲了提⾼性能,將I/O操作重疊。線程可以同時等待不同的I/O操作

2.線程的缺點


a.性能的缺失
b.健壯性低
c.缺乏訪問控制
d.編程才能難度高

四、線程標識


同每個進程有進程id,每個線程也有一個線程id。進程id是整個系統唯一的,但線程只在所屬環境內有效。線程id使用pthread_t表示

//獲得自身線程id
#inlcude <pthread.h>
pthread_t pthread_self(void);
//函數返回值:調用線程的線程id

//比較兩個線程id
int pthread_equal(pthread_t tid1,pthread_t tid2);
//返回值:成功返回非0值,失敗返回0

五、線程的操作


1.創建線程


1.1函數


int pthread_create(pthread_t *threadid,      //線程標識符
           const  pthread_attr_t *attr, //線程屬性,NULL
            void* (*route)(void *arg) ,//線程回調函數
            void *arg )               //線程回調函數參數

//所有的線程函數均通過返回值返回錯誤信息。
成功返回0,失敗返回錯誤碼。

1.2.代碼


 1 #include <stdio.h>
  2 #include <pthread.h>
  3 #include <string.h>
  4 #include <stdlib.h>
  5 
  6 void *rout(void *arg)
  7 {
  8     while(1)
  9     {
 10         printf(" I am thread 1\n");
 11         sleep(1);
 12     }
 13 }
 14 
 15 int main(void)
 16 {
 17     pthread_t tid;
 18     int ret=pthread_create(&tid,NULL,rout,NULL);
 19 
 20     //創建失敗返回錯誤碼
 21     if(ret!=0)
 22     {
 23         fprintf(stderr,"pthread_create: %s\n",strerror(ret));
 24         exit(0);
 25     }
 26 
 27     while(1)
 28     {
 29         printf("I am main thread :%X\n",tid);
 30         sleep(1);
 31     }
 32 }

執行結果:
這裏寫圖片描述

2.syscall


2.1函數


獲得線程id
int syscall(int number,...);

#include <sys/syscall.h>
pid_t tid;
tid=syscall(SYS_gettid);

2.2代碼


1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <pthread.h>
  4 #include <unistd.h>
  5 #include <sys/syscall.h>
  6 
  7 void *route(void *arg)
  8 {
  9     pid_t  tid=syscall(SYS_gettid);//獲得線程id
 10     while(1)
 11     {
 12         printf("tid = %d\n",tid);
 13         sleep(1);
 14     }
 15 }
 16 
 17 int main(void)
 18 {
 19     pthread_t mytid;
 20     pthread_create(&mytid,NULL,route,NULL);
 21     pid_t tid=syscall(SYS_gettid);
 22     while(1)
 23     {
 24         printf("tid = %d\n",tid);
 25         sleep(1);
 26     }
 27 }

結果如下:
這裏寫圖片描述

3.線程等待


爲什麼需要線程等待?
a.已經退出的線程,其空間未被釋放,仍然在進程的地址空間內
c.創建新的線程不會複用剛纔退出的線程的地址空間

int pthread_join(pthread_t tid,void **retval);//傳二級指針
//返回值:成功返回0,否則返回錯誤編號

調⽤該函數的線程將掛起等待,直到id爲thread的線程終⽌。thread線程以不同的⽅法終⽌,通過pthread_join得到的終⽌狀態是不同的,總結如下:
:a.如果thread線程通過return返回,retval所指向的單元⾥存放的是thread線程函數的返回值
b.如果thread線程被別的線程調⽤pthread_ cancel異常終掉tretval所指向的單元⾥存放的是常數PTHREAD_ CANCELED
c.如果thread線程是⾃⼰調⽤pthreadexit終⽌的,retvalr所指向的單元存放的是傳給pthread_exit的參數
d.如果對thread線程的終⽌狀態不感興趣,可以傳NULL給retval參數

4.線程終止


4.1函數


若進程中的任意線程調用了exit,_Exit,那麼整個進程將終止。
線程退出方式
a.線程從啓動例程中返回,返回值是線程的退出碼
b.線程可被同一進程中的其他線程取消
c.線程調用pthread_exit

#include <pthread.h>

void pthread_exit(void *retval);
int pthread_cancel(pthread_t tid);
//注意:cancel的線程不是立即退出,而是等到cancel點系統調用都是cancel點

//人爲添加cancel點
void pthread_testcancel(void);

4.2代碼


//代碼驗證三種終止方式:
  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <pthread.h>
  4 #include <string.h>
  5 #include <stdlib.h>
  6 
  7 void *thread1(void *arg)
  8 {   
  9     printf("thread 1 returning...\n");
 10     int *p=(int*)malloc(sizeof(int));
 11     *p=1;
 12     return (void*)p;
 13 }
 14 
 15 
 16 void *thread2(void *arg)
 17 {   
 18     printf("thread 2 exiting...\n");
 19     int *p=(int*)malloc(sizeof(int));
 20     *p=2;
 21     pthread_exit((void*)p);
 22 }
 25 void *thread3(void *arg)
 26 {
 27     while(1)
 28     {
 29         printf("thread 3 is running...\n");
 30         sleep(1);
 31     }
 32     return NULL;
 33 }
 34 
 35 int main(void)
 36 {
 37     pthread_t tid;
 38     void *ret;
 39     //thread 1 return
 40     pthread_create(&tid,NULL,thread1,NULL);
 41     pthread_join(tid,&ret);
 42     printf("thread return,thread id %X,return code:%d\n",tid,*(int*    )ret);
 43     free(ret);
    free(ret);
 44     
 45     //thread 2 exit
 46     pthread_create(&tid,NULL,thread2,NULL);
 47     pthread_join(tid,&ret);
 48     printf("thread return,thread id %X,return code:%d\n",tid,*(int*    )ret);
 49     free(ret);
 50 
 51 
 52     //thread 3 by other cancel
 53     pthread_create(&tid,NULL,thread3,NULL);
 54     sleep(3);
 55     pthread_cancel(tid);
 56     pthread_join(tid,&ret);
 57     if(ret==PTHREAD_CANCELED)
 58         printf("thread return,thred id %X,return code:PTHREAD_CANCE    LED\\n",tid);
 59     else
 60         printf("thread return ,thread id %X,return code:NULL\n",tid    );
 61 }

執行結果如下:
這裏寫圖片描述

//2.cancel函數如何使用
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

void *route(void *arg)
{
    while ( 1 ) {
        pthread_testcancel();
        // printf(" I'm active\n");
        // sleep(1);
    }
}

int main( void )
{
    pthread_t tid;
    pthread_create(&tid, NULL, route, NULL);

    sleep(2);
    pthread_cancel(tid);
    printf("%x thread dead\n", tid);
    pthread_exit(NULL);
}

結果:
這裏寫圖片描述

5.pthread_cleanup_push


線程清理處理程序

5.1函數


#include <pthread.h>
 void pthread_cleanup_push(void (*routine)(void *),void *arg);
 void pthread_cleanup_pop(int execute);

當線程執行以下動作時,調用該函數:
a.調用pthread_exit時
b.響應取消請求時
c.用非零參數(execute)調用 pthread_cleanup_pop時
若execute爲0時,清理函數不被調用,二者匹配使用

5.2代碼


  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <pthread.h>
  4 #include <string.h>
  5 #include <stdlib.h>
  6 
  7 void cleanup(void *arg)
  8 {   
  9     printf("cleanup: %s\n",(char*)arg);
 10 }
 11 
 12 void *thread1(void* arg)
 13 {   
 14     printf("thread1 start\n");
 15     pthread_cleanup_push(cleanup,"thread 1 first handler");
 16     
 17     pthread_cleanup_push(cleanup,"thread 1 second handler");
 18     
 19     printf("thread1 push finish\n");
 20     if(arg)
 21     {   
 22         return ((void*)1);
 23     }
 24     pthread_cleanup_pop(0);
 25 
 26     pthread_cleanup_pop(0);
 27     return ((void*)1);
 28 }
 29 
 30 
 31 
 32 void *thread2(void* arg)
 33 {
 34     printf("thread2 start\n");
 35     pthread_cleanup_push(cleanup,"thread 2 first handler");
 36 
 37     pthread_cleanup_push(cleanup,"thread 2 second handler");
 38 
 39     printf("thread 2 push finish\n");
 40 
 41     if(arg)
 42     {
 43         pthread_exit ((void*)2);
 44     }
 45     pthread_cleanup_pop(0);
 46 
 47     pthread_cleanup_pop(0);
 48     pthread_exit ((void*)2);
 49 }
 50 int main(void)
 51 {
 52     int ret;
 53     void *tmp;
 54     pthread_t tid1,tid2;
 55     ret=pthread_create(&tid1,NULL,thread1,(void*)1);
 56     pthread_join(tid1,&tmp);
 57     ret=pthread_create(&tid2,NULL,thread2,(void*)1);
 58     printf("thread 1 exit code %d\n",(int)tmp);
 59 
 60     pthread_join(tid2,&tmp);
 61     printf("thread 2 exit code %d\n",(int)tmp);
 62     exit(0);
 63 
 64 }

結果如下:
這裏寫圖片描述

可以看到只調用了第二個線程的清理處理程序,因此如果線程是通過從他的啓動例程返回,那麼它的清理處理程序不會被調用,清理處理程序是按照與其安裝時的相反順序調用。

發佈了128 篇原創文章 · 獲贊 51 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章