Linux 多線程學習 .

一、多線程 VS 多進程

     和進程相比,線程有很多優勢。在Linux系統下,啓動一個新的進程必須分配給它獨立的地址空間,建立衆多的數據表來維護代碼段和數據。而運行於一個進程中的多個線程,他們之間使用相同的地址空間。正是這樣,同一進程下的線程之間共享數據空間,數據可以相互使用,並且線程間切換也要更快些,可以更有效的利用CPU。

二、程序設計

[注] 頭文件<pthread.h> 編譯時要加載動態庫 libpthread.a,使用 -lpthread

 

1、創建線程

2、等待線程

3、關閉線程

4、退出清除

 

1、創建線程

         int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, void *(*start_rtn)(void), void *arg)

 

tidp爲線程id,是函數分配的值,所以要傳一個 pthread_t 的地址。

attr線程屬性,通常爲空。

start_rtn爲線程要執行的函數,返回值爲空指針,參數爲後面的*arg

若成功則返回0,否則返回出錯編號。

 

例:

  1. #include<stdio.h>   
  2. #include<pthread.h>   
  3.   
  4. void *func1(void *arg){        //原函數聲明   
  5.         int i;  
  6.         for(i=0;i<5;i++){  
  7.                 printf("this is func1! The num is %d\n",*(int*)arg);    //將空指針轉換爲int型指針   
  8.                 sleep(1);  
  9.         }  
  10. }  
  11.   
  12. void *func2(int *m){ //自定義類型聲明,也可以定義非指針類型,但是在create時會有警告,因爲非地址並不能改變傳入的值   
  13.         int i;  
  14.         for(i=0;i<5;i++){  
  15.                 printf("this is func2! The num is %d\n",*m);  
  16.                 (*m)++;  
  17.                 sleep(1);  
  18.         }  
  19. }  
  20.   
  21. int main(){  
  22.         pthread_t id1,id2;  
  23.         int num = 5;  
  24.         int *p = &num;  
  25.         if(pthread_create(&id1,NULL,(void *)func1,(void *)p) != 0){  
  26.                 printf("thread1 create error!\n");  
  27.                 return -1;  
  28.         }  
  29.         if(pthread_create(&id2,NULL,(void *)func2,&num) != 0){  
  30.                 printf("thread2 create error!\n");  
  31.                 return -1;  
  32.         }  
  33.         pthread_join(id1,NULL);     //等待線程結束      
  34.         pthread_join(id2,NULL);  
  35.         printf("Running complete!\n");  
  36.         return 0;  
  37.   
  38.   
  39. }  
#include<stdio.h>
#include<pthread.h>

void *func1(void *arg){        //原函數聲明
        int i;
        for(i=0;i<5;i++){
                printf("this is func1! The num is %d\n",*(int*)arg);    //將空指針轉換爲int型指針
                sleep(1);
        }
}

void *func2(int *m){ //自定義類型聲明,也可以定義非指針類型,但是在create時會有警告,因爲非地址並不能改變傳入的值
        int i;
        for(i=0;i<5;i++){
                printf("this is func2! The num is %d\n",*m);
                (*m)++;
                sleep(1);
        }
}

int main(){
        pthread_t id1,id2;
        int num = 5;
        int *p = &num;
        if(pthread_create(&id1,NULL,(void *)func1,(void *)p) != 0){
                printf("thread1 create error!\n");
                return -1;
        }
        if(pthread_create(&id2,NULL,(void *)func2,&num) != 0){
                printf("thread2 create error!\n");
                return -1;
        }
        pthread_join(id1,NULL);     //等待線程結束   
        pthread_join(id2,NULL);
        printf("Running complete!\n");
        return 0;


}

運行結果:

  1. [fsy@localhost process]$ gcc thC.c -o thC -lpthread -g  
  2. [fsy@localhost process]$ ./thC  
  3. this is func2! The num is 5  
  4. this is func1! The num is 6  
  5. this is func2! The num is 6  
  6. this is func1! The num is 7  
  7. this is func2! The num is 7  
  8. this is func1! The num is 8  
  9. this is func2! The num is 8  
  10. this is func1! The num is 9  
  11. this is func2! The num is 9  
  12. this is func1! The num is 10  
  13. Running complete!  
  14. [fsy@localhost process]$  
[fsy@localhost process]$ gcc thC.c -o thC -lpthread -g
[fsy@localhost process]$ ./thC
this is func2! The num is 5
this is func1! The num is 6
this is func2! The num is 6
this is func1! The num is 7
this is func2! The num is 7
this is func1! The num is 8
this is func2! The num is 8
this is func1! The num is 9
this is func2! The num is 9
this is func1! The num is 10
Running complete!
[fsy@localhost process]$


2、等待線程

  [注]當調用pthread_create函數時,線程並沒有開始執行,主進程應有等待,比如用sleep,或者用更專業的函數:pthread_join

    int pthread_join(pthread_t tid, void **rval_ptr)

 

調用函數可以阻塞調用線程,直到指定的線程終止。

tid爲等待退出線程的id,rval_ptr爲函數的返回值。是指向指針的指針,可以置空。

 

例:

  1. #include<stdio.h>   
  2. #include<pthread.h>   
  3. #include<stdlib.h>   
  4.   
  5. void *func(int *p){  
  6.         int *num=(int *)malloc(sizeof(int));     //必須動態創建,原因可以參考我動態分配內存的博客   
  7.         printf("Please input the number: ");  
  8.         scanf("%d",num);  
  9.         return (void *)num;              //類型是pthread_create的參數規定的   
  10. }  
  11.   
  12. int main(){  
  13.         pthread_t pth;  
  14.         void *a;  
  15.         if(pthread_create(&pth,NULL,(void *)func,NULL) != 0){  
  16.                 printf("create thread error!\n");  
  17.                 return 1;  
  18.         }  
  19.         pthread_join(pth,&a);          //指向空指針的指針   
  20.         printf("get the num from the thread, it's %d\n",*(int *)a);  
  21.         return 0;  
  22. }  
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>

void *func(int *p){
        int *num=(int *)malloc(sizeof(int));     //必須動態創建,原因可以參考我動態分配內存的博客
        printf("Please input the number: ");
        scanf("%d",num);
        return (void *)num;              //類型是pthread_create的參數規定的
}

int main(){
        pthread_t pth;
        void *a;
        if(pthread_create(&pth,NULL,(void *)func,NULL) != 0){
                printf("create thread error!\n");
                return 1;
        }
        pthread_join(pth,&a);          //指向空指針的指針
        printf("get the num from the thread, it's %d\n",*(int *)a);
        return 0;
}

 

 

3、終止線程

線程終止有以下三種方式:

1、線程從函數中返回

2、線程可以別其他函數終止

3、線程自己調用pthread_exit函數

void pthread_exit(void *rval_ptr)

rval_ptr爲線程退出返回值的指針,即函數返回值。

4、退出清除

       void pthread_cleanup_push(void (*rtn)(void*), void *arg)

 

rtn爲清除函數,arg是清除函數的參數

void pthread_cleanup_pop(int execute)

當execute 非0時執行清除函數。爲0時不執行。

  從pthread_cleanup_push的調用點到pthread_cleanup_pop之間的程序段中,如果有終止進程的動作,如調用pthread_exit或異常終止(不包括return),就會執行pthread_cleanup_push()所指定的清理函數。多個嵌套匹配時,就近匹配。

 

例:

  1. #include<stdio.h>   
  2. #include<pthread.h>   
  3.   
  4. void *clean(char *argv){  
  5.         printf("clean is called by %s\n",argv);  
  6.         return NULL;  
  7. }  
  8. void *func1(void *argv){  
  9.         printf("welcome enter the func1!\n");  
  10.         pthread_cleanup_push((void*)clean,"the first time call!");  
  11.         pthread_cleanup_push((void*)clean,"the second time call!");  
  12.         if(argv){  
  13.                 return (void *)1;            //第二次運行將此句注掉   
  14.         }  
  15.         pthread_cleanup_pop(0);  
  16.         pthread_cleanup_pop(1);  
  17.         return (void *)0;  
  18. }  
  19.   
  20. void *func2(void *argv){  
  21.         sleep(1);             //兩個線程運行先後不確定   
  22.         printf("welcome enter the func2!\n");  
  23.         pthread_cleanup_push((void*)clean,"the first time call!");  
  24.         pthread_cleanup_push((void*)clean,"the second time call!");  
  25.         if(argv){  
  26.                 pthread_exit(NULL);  
  27.         }  
  28.         pthread_cleanup_pop(0);  
  29.         pthread_cleanup_pop(0);  
  30.         return (void *)0;  
  31. }  
  32.   
  33.   
  34. int main(){  
  35.         pthread_t tid1,tid2;  
  36.         if(pthread_create(&tid1,NULL,(void *)func1,(void *)1) != 0){  
  37.                 printf("thread1 create error!\n");  
  38.                 return 1;  
  39.         }  
  40.   
  41.          if(pthread_create(&tid2,NULL,(void *)func2,(void *)1) != 0){  
  42.                 printf("thread2 create error!\n");  
  43.                 return 1;  
  44.         }  
  45.         pthread_join(tid1,NULL);  
  46.         pthread_join(tid2,NULL);  
  47.         return 0;  
  48. }  
#include<stdio.h>
#include<pthread.h>

void *clean(char *argv){
        printf("clean is called by %s\n",argv);
        return NULL;
}
void *func1(void *argv){
        printf("welcome enter the func1!\n");
        pthread_cleanup_push((void*)clean,"the first time call!");
        pthread_cleanup_push((void*)clean,"the second time call!");
        if(argv){
                return (void *)1;            //第二次運行將此句注掉
        }
        pthread_cleanup_pop(0);
        pthread_cleanup_pop(1);
        return (void *)0;
}

void *func2(void *argv){
        sleep(1);             //兩個線程運行先後不確定
        printf("welcome enter the func2!\n");
        pthread_cleanup_push((void*)clean,"the first time call!");
        pthread_cleanup_push((void*)clean,"the second time call!");
        if(argv){
                pthread_exit(NULL);
        }
        pthread_cleanup_pop(0);
        pthread_cleanup_pop(0);
        return (void *)0;
}


int main(){
        pthread_t tid1,tid2;
        if(pthread_create(&tid1,NULL,(void *)func1,(void *)1) != 0){
                printf("thread1 create error!\n");
                return 1;
        }

         if(pthread_create(&tid2,NULL,(void *)func2,(void *)1) != 0){
                printf("thread2 create error!\n");
                return 1;
        }
        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
        return 0;
}

 

運行結果:

  1. [fsy@localhost process]$ gcc thClean.c -o thclean -lpthread  
  2. [fsy@localhost process]$ ./thclean  
  3. welcome enter the func1!  
  4. welcome enter the func2!  
  5. clean is called by the second time call!          //此處先2後1  
  6. clean is called by the first time call!  
  7. [fsy@localhost process]$ vim thClean.c  
  8. [fsy@localhost process]$ gcc thClean.c -o thclean -lpthread  
  9. [fsy@localhost process]$ ./thclean  
  10. welcome enter the func1!  
  11. clean is called by the first time call!          //second已經被pop  
  12. welcome enter the func2!  
  13. clean is called by the second time call!  
  14. clean is called by the first time call!  
  15. [fsy@localhost process]$   
發佈了0 篇原創文章 · 獲贊 8 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章