第3篇 Linux系統編程--線程生命週期與狀態

在上一篇中,我們已經通過知道如何創建多個線程,本篇會談論到線程的生命週期,在此之前應該瞭解一下線程在一個進程中的內存佈局

主線程和線程棧

每個棧都是一個獨立的虛擬內存分配,可以將其放置在任意位置。重要的是要注意,棧的大小通常是有限的。操作系統保留一定的最大的尺寸例如1MB或8MB)。棧不能超過該大小。但是當固定空間用完時,將觸發棧溢出。在實踐中這不是問題。實際上,超過合理的棧尺寸被認爲是一個錯誤。而由多個段組成。只需添加更多細分即可任意增長。堆由用戶模式的C庫管理。內核對此一無所知。內核所做的只是在任意的位置提供虛擬內存。

什麼是虛擬內存(Virtual Memory),這是涉及到系統內核的內存管理方式足以用一個系列的文章去講解,以後有機會再說,或者你可以參考相關的文章。

線程棧只是進程所佔據的虛擬內存中的一個連續塊。 它的最大大小是固定的。 可能看起來像下面的圖:

進程中的線程棧佈局

我們知道,任何C/C++程序運行時,首先運行main函數,在多線程編程中,這個main函數也被稱作主線程函數.

  • 1)主線程可以通過pthread_create系統調用創建子線程。
  • 2)主線程和其他子線程獲取在整個CPU時鐘週期中獲得資源調度是均等的,也就是說所有線程默認狀態下是異步獨立運行的。
  • 3)主線程的確定了所有子線程的生命週期,一但主線程返回或者終止,其他子線程也會終止。
  • 4)主線程接受參數得的方式是通過argc和argv,而普通的線程只有一個參數void*類型的指針
  • 5)一般來說,主線程默認在棧中可以達到足夠的長度,而普通的線程棧的是受到限制的。
  • 6)主線程伴隨着進程的創建而創建.

線程的生命週期

下面是一個示例,我們在主線程函數暫停5秒,在創建的子線程函數中,我們暫停2秒,目地是檢驗上面線程的特徵的第2點和第3點。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include<string.h>

typedef struct{
    int age;
    char name[15];
} Person;


void *say_hello(void* per){
    sleep(2);
    Person* p=(Person*)per;
    printf("Hi!我的名字叫%s,今年%d歲\n",p->name,p->age);
    return (void*)0;
}
int main(int argc,char* argv){
      pthread_t tid;
      int err;

      Person s;
      memcpy(s.name,"Peter",15);
      s.age=25;
      err=pthread_create(&tid,NULL,say_hello,(void*)(&s));
      if(err!=0){
            printf("創建線程失敗\n");
            return 0;
      }

      printf("主線程運行中!!\n");
      sleep(5);
      return 0;
}

Ok,我們的主線程先於子線程打印字符串,而字符串在調用pthread_create就已經運行,但它被延遲2秒才輸出。這樣就證明所有線程是獨立運行的。


如果你我們修改一下上面的實例代碼,將main函數中的sleep(5)註釋,我們能夠看到,子線程沒有完全執行之前,主線程已經退出了,從而也強迫子線程跟隨終止。

但有時我們的確沒必要讓主線程等待子線程執行完成後才退出的。使用pthread_exit系統調用可以做到主線程自己執行完後自己退出了(注意:進程沒有退出),而子線程仍然可以獨立運行直到其退出。

int main(int argc,char* argv){
      pthread_t tid;
      int err;

      Person s;
      memcpy(s.name,"Peter",15);
      s.age=25;
      err=pthread_create(&tid,NULL,say_hello,(void*)(&s));
      if(err!=0){
            printf("創建線程失敗\n");
            return 0;
      }

      printf("主線程運行中!!\n");
      int *retval;
      pthread_exit(retval);

線程狀態

我們會繼續探討一下線程在運行時的四種狀態。

  • 準備等待可用的CPU資源,其他條件一切準備好。當線程被pthread_create創建時或者阻塞狀態結束後就處於準備狀態。
  • 運行 :線程已經獲得CPU的使用權,並且正在運行,在多核心的機器中同時存在多個線程正在運行。如果這種情況不加以控制,會造成整個程序沒響應。
  • 阻塞:指一個線程在執行過程中暫停,以等待某個條件的觸發。
    • 例如線程可能在處理有關I/O的任務,可能I/O設備繁忙尚未響應或沒有可用的I/O緩存。
    • 也可能當前線程等待一個可用的條件便來變量。
    • 錯誤地對一個已被鎖住的互斥量加鎖
    • 調用sigwait等待尚未發生的信號。
  • 終止:線程已經從回調函數中返回,或者調用pthread_exit返回,或者被強制終止。

線程運行時的狀態切換

 

小結

我們本篇討論了主線程和子線程的關係,下一篇我們會討論線程在運行時的狀態。

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