天天看點

77-線程建立與終止

在​​《線程初識》​​​中,我們已經了解到了建立線程的函數 ​

​pthread_create​

​​ 函數。本篇打算詳細介紹一下​

​pthread_create​

​函數,另外還有線程終止及相關的函數。

1. 線程建立

  • 函數原型
typedef void *(*start_routine) (void *);

int pthread_create(pthread_t *thread, const      
  • pthread_t 類型是線程 id 的類型,它可能是個整數(大多數是這樣),也可能是結構體,這取決于具體實作。thread 參數是一個傳出參數,它主要用來接收被建立的線程的 id 号。線上程執行過程中調用函數​

    ​pthread_self​

    ​函數,可以擷取到自己的線程 id 号。
  • pthread_attr_t 類型是用來儲存線程屬性的結構體,通常參數 attr 可以傳 NULL,表示建立具有預設屬性的線程。有關線程的屬性結構體,後文會作具體講解。
  • start_routine 是用來聲明函數型如​

    ​void* th_fn(void* arg)​

    ​ 這樣的函數的,它是線程過程函數。線程被建立後,一旦被排程,就會進入到函數 th_fn 中執行。
  • 最後一個參數 arg,是線程過程函數 th_fn 的參數。
  • ​pthread_create​

    ​​ 函數的傳回值是一個錯誤碼,傳回 0 表示成功。和其它 POSIX 函數不同的是,​

    ​pthread_create​

    ​ 函數執行錯誤并不會設定 errno 變量。

2. 線程終止

2.1 線程終止方式

線程的終止包括兩大類,主動終止和被動終止。

  • 主動終止
  • 線程過程函數執行​

    ​return​

    ​ 正常傳回,傳回值是線程的退出碼
  • 線種過程函數執行​

    ​pthread_exit​

    ​ 函數退出,其參數是線程退出碼
  • 被動終止
  • 在其它線程中調用​

    ​pthread_cancel​

    ​ 函數
  • 任意線程調用 exit、_Exit 或者 _exit 導緻整個程序終止

2.2 pthread_exit 函數

pthread_exit 函數原型

void pthread_exit(void      

該函數的參數是線程的退出碼,它是無類型的指針。通過函數 ​

​pthread_join​

​ 可以擷取到這個指針的值。該函數有點類似于我們之前學習的 waitpid 函數,它可以回收線程所使用的資源。

int pthread_join(pthread_t tid, void      

調用​

​pthread_join​

​​ 會阻塞目前線程,直到指定的線程 tid 傳回(return)、執行 ​

​pthread_exit​

​​ 或者被其它線程取消(​

​pthread_cancel​

​)。rval_ptr 是一個輸出參數,它用來接收線程退出碼(是一個 void* 類型)。

2.3 pthread_cancel 函數

int      

預設情況下,該函數的效果相當于線上程 tid 中調用 ​

​pthread_exit((void*)PTHREAD_CANCEL)​

​ 函數。

3. 示例(線程主動退出)

  • 程式代碼
// threadexit.c
#include <unistd.h>                                                                                                   
#include <pthread.h>                                                                                                  
#include <stdio.h>                                                                                                    
#include <stdlib.h>                                                                                                   
#include <string.h>                                                                                                   

#define ERR(name,err) do{printf("%s:%s\n",#name,strerror(err));exit(-1);}while(0);                                    

void* th_fn1(void* arg) {                                                                                             
  puts("thread 1 returning");                                                                                         
  return (void*)10;                                                                                                   
}                                                                                                                     

void* th_fn2(void* arg) {                                                                                             
  puts("thread 2 exiting");                                                                                           
  pthread_exit((void*)20);                                                                                            
  puts("thread 2 exited");                                                                                            
}                                                                                                                     

int main() {                                                                                                          
  pthread_t tid1, tid2;                                                                                               
  int err;                                                                                                            
  void* ret;                                                                                                          

  err = pthread_create(&tid1, NULL, th_fn1, NULL);                                                                    
  if (err != 0) ERR(pthread_create1, err);                                                                            

  err = pthread_create(&tid2, NULL, th_fn2, NULL);                                                                    
  if (err != 0) ERR(pthread_create2, err);                                                                            

  sleep(2);                                                                                                           

  // 主線程阻塞直到線程 tid1 退出                                                                                                                  
  err = pthread_join(tid1, &ret);                                                                                     
  if (err != 0) ERR(pthread_join1, err);                                                                              
  printf("thread 1 exit code %d\n", (int)ret);                                                                        

  // 主線程阻塞直到線程 tid2 退出                                                                                                                     
  err = pthread_join(tid2, &ret);                                                                                     
  if (err != 0) ERR(pthread_join2, err);                                                                              
  printf("thread 2 exit code %d\n", (int)ret);                                                                        

  return 0;                                                                                                           
}        
  • 編譯和運作
$ gcc threadexit.c -o threadexit -lpthread
$ ./threadexit      
  • 運作結果
77-線程建立與終止

圖1 線程主動終止

4. 示例(被動終止)

  • 代碼
// threadcancel.c
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ERR(name,err) do{printf("%s:%s\n",#name,strerror(err));exit(-1);}while(0);

void* th_fn1(void* arg) {
  while(1) {
    puts("thread 1 running");
    sleep(1);
  }
  return (void*)10;
}

void* th_fn2(void* arg) {
  while(1) {
    puts("thread 2 running");
    sleep(1);
  }
  pthread_exit((void*)20);
}

int main() {
  pthread_t tid1, tid2;
  int err;
  void* ret;

  err = pthread_create(&tid1, NULL, th_fn1, NULL);
  if (err != 0) ERR(pthread_create1, err);

  err = pthread_create(&tid2, NULL, th_fn2, NULL);
  if (err != 0) ERR(pthread_create2, err);

  sleep(5);

  // 通知 tid1 和 tid2 退出。
  pthread_cancel(tid1);
  pthread_cancel(tid2);

  err = pthread_join(tid1, &ret);

  // 線程退出碼都是 PTHREAD_CANCELED
  printf("PTHREAD_CANCELED = %d\n", (int)PTHREAD_CANCELED);
  if (err != 0) ERR(pthread_join1, err);
  printf("thread 1 exit code %d\n", (int)ret);

  err = pthread_join(tid2, &ret);
  if (err != 0) ERR(pthread_join2, err);
  printf("thread 2 exit code %d\n", (int)ret);

  return 0;
}      
  • 編譯和運作
$ gcc threadcancel.c -o threadcancel -lpthread
$ ./threadcancel      
  • 運作結果
77-線程建立與終止

5. 總結

  • 掌握線程建立函數
  • 掌握線程終止的幾種方式