多線程API - 小白如何快速上手併發編程

多線程API - 小白如何快速上手併發編程

  • 線程的基本概念

    • 一.線程和進程的區別(linux環境下)
      • 進程是資源管理的最小單位,線程是系統執行的最小單位
      • 線程和進程都有對應的PCB和TCB,在linux內核中創建線程和進程使用的底層函數和進程一樣,都是clone
      • 進程可以蛻化成線程,一個進程可以有一個至多個線程
      • 進程有自己獨享的0-4G虛擬地址空間,而線程共享同一進程的地址空間
    • 二.線程有共享也有獨佔
      • 線程共享的資源有:
        • 1.文件描述符表
        • 2.每種信號的處理方式
        • 3.當前工作目錄
        • 4.用戶ID和組ID
        • 5.內存地址空間 (.text/.data/.bss/heap/共享庫)
      • 線程獨佔的資源有:
        • 1.線程id
        • 2.計數器和棧空間(內核棧和用戶空間棧)
        • 3.信號屏蔽字
        • 4.errno變量
        • 5.調度優先級
    • 三.線程的優缺點
      • 優點
        • 1.提高程序併發性
        • 2.開銷小
        • 3.數據通信、共享數據方便
      • 缺點
        • 1.線程所有操作函數 pthread_* 是庫函數,而非系統調用,不夠穩定
        • 2.編寫調試困難,不支持gdb
        • 3.對信號支持性差
  • 使用線程的API

    • 線程常見的函數有以下幾種
      • pthread_self函數 - 獲取線程id
      • pthread_create函數 - 創建一個新線程
      • pthread_exit函數 - 將單個線程退出
      • pthread_join函數 - 阻塞等待線程退出並獲取線程退出狀態
      • pthread_cancel函數 - 殺死(取消)線程
      • pthread_detach函數 - 實現線程分離
      • pthread_equal函數 - 比較線程id是否相等(系統留用,看以後pthread_t是否改爲結構體)
    • 線程API和進程API對比
      • pthread_self - getpid
      • pthread_create - fork
      • pthread_exit - exit
      • pthread_join - wait
      • pthread_cancel - kill
  • 線程控制原語

    • pthread_t pthread_self(void); ---------使用見線程實例1

      • 功能 : 獲取線程id

      • 返回值:成功:0; 失敗:NULL

        • 線程ID:pthread_t類型,本質:在Linux下爲無符號整數(%lu),可以理解爲typedef unsigned long int pthread_t;

        • notes

          • 線程id是進程內識別不同線程的標誌,也意味着兩個進程間有允許使用相同的線程id

          • 在獲取線程id時,避免使用全局變量。在主線程可通過pthread_create第一個參數傳出,在子線程通過調用pthread_self()得到id.

      • int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); -----使用見線程實例1

        • 功能 :創建一個新線程

        • 返回值:成功:0; 失敗:錯誤號 -----Linux環境下,所有線程特點,失敗均直接返回錯誤號。

        • parameters

          • 參數1:傳出參數,保存系統爲我們分配好的線程ID

            • 參數2:通常傳NULL,表示使用線程默認屬性。若想使用具體屬性也可以修改該參數。

            • 參數3:函數指針,指向線程主函數(線程體),該函數運行結束,則線程結束。

            • 參數4:線程主函數執行期間所使用的參數==,若線程需要多個參數,可傳入結構體變量==

      • void pthread_exit(void *retval); ------ 使用見線程實例3

        • 功能 : 將單個線程退出

        • parameter

          • retval表示線程退出狀態,通常傳NULL
        • notes

          • 不能使用exit退出指定的線程,因爲exit是退出進程的,會將該進程所有線程退出

          • 線程的三種退出方式

            • return
            • pthread_exit()
            • pthread_cancel()
    • int pthread_join(pthread_t thread, void **retval); ------使用見線程實例4

      • 功能 : 阻塞等待線程退出並獲取線程退出狀態

      • parameters

        • 參數1 : 需要等待的線程id
        • 參數2 : 獲取線程id退出狀態
      • notes

        • 如果thread線程通過return返回,retval所指向的單元裏存放的是thread線程函數的返回值.

        • 如果thread線程被別的線程調用pthread_cancel異常終止掉,retval所指向的單元裏存放的是常數PTHREAD_CANCELED(一般爲-1)。

        • 如果thread線程是自己調用pthread_exit終止的,retval所指向的單元存放的是傳pthread_exit的參數

        • 如果對線程退出狀態沒有興趣,可以對retval傳空。

    • int pthread_cancel(pthread_t thread); ------使用見線程實例4

      • 功能 : 殺死(取消)線程

      • 成功:0;失敗:錯誤號

      • notes

        • 線程取消不是立馬執行的,需要一定的時間。需要線程進入系統調用creat,open,pause,close,read,write等,纔可退出,若線程中沒有系統調用,需要添加pthread_testcancel()函數斷點,否則線程死循環
    • int pthread_detach(pthread_t thread); ------使用見線程實例5

      • 功能 : 實現線程分離

      • 返回值:0;失敗:錯誤號

      • notes

        • 分離態線程一旦終止就立刻回收它佔用的所有資源,而不保留終止狀態,避免殭屍線程。不能對一個已經處於detach狀態的線程調用pthread_join,這樣的調用將返回EINVAL錯誤(一般爲22)。
    • int pthread_equal(pthread_t t1, pthread_t t2);//幾乎不用

      • 功能 : 獲取線程id

  • 線程實例

    • 1.創建一個子線程,子線程打印出自己的線程id

      #include <stdio.h>
      #include <unistd.h>
      #include <pthread.h>
      
      void *thread1(void *arg)
      {
              printf("my thread_id is %lu\n",pthread_self());
              return NULL;
      }
      
      int main(int args, char *argv[])
      {
              pthread_t tid;//pthread is equal to unsigned long in linux
      
              pthread_create(&tid, NULL, thread1, NULL);//第一個NULL表示默認屬性,第二個是線程執行時所需要的參數
              sleep(1);//wait thread end
      
              return 0;
      }
      
      • 編譯

      在這裏插入圖片描述

      • 運行

      在這裏插入圖片描述

    • 2.循環創建多個子線程,並使用pthread_exit退出其中i = 3的線程

      #include <stdio.h>
      #include <unistd.h>
      #include <pthread.h>
      
      void *thread(void *arg)
      {
              int i = 0;
              i = (int)arg;
      
              if(3 == i)//3 thread exit
              {
                      pthread_exit(NULL);
              }
              sleep(i);//keep thread write order
              printf("I am %d thead, my thread id is %lu\n", i, pthread_self());
      
      }
      
      int main(int args, char *arg[])
      {
              pthread_t tid = 0;
              int i = 0;
      
              for(i=0; i<5; i++)
              {
                      pthread_create(&tid, NULL, thread, (void*)i);
              }
              sleep(i);//wait thread end
      
              return 0;
      }
      
      
      • 編譯

      在這裏插入圖片描述

      • 運行

      在這裏插入圖片描述

    • 3.創建多個線程並使用pthread_exit退出,打印出join保存的線程退出狀態

      #include <stdio.h>
      #include <unistd.h>
      #include <stdlib.h>
      #include <pthread.h>
      
      typedef struct node
      {
              int a;
              int b;
      }LNODE;
      
      void *thread(void *arg)
      {
              LNODE *node1;
              node1 = (LNODE*)malloc(sizeof(struct node));
              if(NULL == node1)
              {
                      printf("no enough memory malloc\n ");
                      return NULL;
              }
      
              node1->a = 100;
              node1->b = 200;
      
              pthread_exit((void *)node1);//如果thread線程是自己調用pthread_exit終止的,retval所指向的單元存放的是傳給pthread_exit的參數
      }
      
      int main(int args,char *argv[])
      {
              pthread_t tid = 0;
              LNODE *node2 = NULL;
      
              pthread_create(&tid, NULL, thread, NULL);
              pthread_join(tid, (void**)&node2);
              printf("node->a is %d, node->b = %d\n", node2->a, node2->b);
      
              free(node2);
              node2 = NULL;
      
              return 0;
      }
      
      
      • 編譯

      在這裏插入圖片描述

      • 運行

      在這裏插入圖片描述

    • 4.創建三個進程,分佈使用不同的退出方式進行測試(return, pthread_exit, pthread_cancel)

      #include <stdio.h>
      #include <stdlib.h>
      #include <pthread.h>
      
      int *thread1(void *arg)//return
      {
              printf("I am thread1, my tid is %lu\n", pthread_self());
      
              return 111;
      }
      
      void *thread2(void *arg)//pthread
      {
              printf("I am thread2, my tid is %lu\n", pthread_self());
      
              pthread_exit((void *)222);
      }
      
      void *thread3(void *arg)//cancel
      {
              int i = 0;
              while(1)
              {
                      i++;
                      pthread_testcancel();
              }
      
              return (void *)333;
      }
      
      int main(int args, char *argv[])
      {
              pthread_t tid = 0;
              void *pointer = NULL;
      
              pthread_create(&tid, NULL, (void *)thread1, NULL);
              pthread_join(tid, &pointer);
              printf("thread1 return value is %d\n", (int)pointer);
      
              pthread_create(&tid, NULL, thread2, NULL);
              pthread_join(tid, &pointer);
              printf("thread2 return value is %d\n", (int)pointer);
      
              pthread_create(&tid, NULL, thread3, NULL);
              pthread_cancel(tid);
              pthread_join(tid, &pointer);
              printf("thread3 return value is %d\n", (int)pointer);
      
              return 0;
      }
      
      • 編譯

      在這裏插入圖片描述

      • 運行

        • thread增加取消點,線程退出

          在這裏插入圖片描述

        • thread3不增加取消點(code_line25 //pthread_testcancel()),線程死鎖

          在這裏插入圖片描述

    • 5.設置線程爲detach遊離態,使用pthread_join判斷線程退出狀態

      #include <stdio.h>
      #include <unistd.h>
      #include <pthread.h>
      
      void *thread(void *arg)
      {
      
              printf("I am thread, my tid is %lu\n", pthread_self());
      
              pthread_exit((void *)111);
      }
      
      int main(int args, char *argv[])
      {
              pthread_t tid = 0;
              int error_flag = 0;
              void *status_pointer = NULL;
      
              pthread_create(&tid, NULL, thread, NULL);
              pthread_detach(tid);
              error_flag = pthread_join(tid, &status_pointer);
              printf("thread value is %d\n", (int)status_pointer);
              printf("error_flag is %d\n", error_flag);
      
              return 0;
      }
      
      • 編譯

      在這裏插入圖片描述

      • 運行
    • line21將線程分離,可見detach後,線程退出後自動回收,其狀態無法再用join獲得

      在這裏插入圖片描述

    • line21註釋,保持線程combination,正常獲取線程狀態111

      在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章