在上一篇中,我們已經通過知道如何創建多個線程,本篇會談論到線程的生命週期,在此之前應該瞭解一下線程在一個進程中的內存佈局
主線程和線程棧
每個棧都是一個獨立的虛擬內存分配,可以將其放置在任意位置。重要的是要注意,棧的大小通常是有限的。操作系統保留一定的最大的尺寸例如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返回,或者被強制終止。
線程運行時的狀態切換
小結
我們本篇討論了主線程和子線程的關係,下一篇我們會討論線程在運行時的狀態。