線程建立時,系統會分配給線程一些資源,我們可以看到的就是線程描述符,線程堆棧,在系統內部還會有更複雜的系統維護一些資訊,線上程建立時,核心總會為其維護一些資源,比較理想的情況是線程運行結束後,釋放系統資源和進程資源,包含線程傳回值佔用的記憶體,線程堆棧,寄存器狀態等等,以備後來者的使用.
線程執行結束後釋放資源的三種方法:
利用這些方法,我們可以避免線程退出時,系統資源仍然無法釋放的情況:
- pthread_join,這個函數的原型是pthread_join(pthread_t thread, void **value_ptr),作用是使調用函數的線程阻塞等待指定線程退出。只有線程是可接入的時候,調用pthread_join才會成功。
- pthread_detach,這個函數可以改變線程的可接入屬性,將其改變成分離模式。當線程處於分離模式,表明我們對於線程的傳回值並不關心,背景工作執行緒執行完畢後會自行退出,核心會釋放該線程佔用的資源。
- 設定線程屬性結構pthread_attr_t為可分離的,如pthread_attr_init(&attr);pthread_attr_setdetach(&attr,PTHREAD_CREATE_DETACHED),並用此線程屬性對象參與pthread_create函數中,這樣建立出的線程都是分離的。
下面是 使用分離線程的意義:
識別泄漏
如果您建立一個可接合的線程,但是忘記聯結它,其資源或私人記憶體一直儲存在進程空間中,從未進行回收再利用。一定要聯結可接
合的線程;否則,可能會引起嚴重的記憶體流失問題。
例如,Red Hat Enterprise Linux (RHEL4)上的一個線程需要一個 10MB 的堆棧,這意味著,如果不聯結它,會有至少
10MB 的記憶體流失。假設您設計一個管理器-背景工作執行緒模式的程式來處理傳入的請求。然後需要建立越來越多的背景工作執行緒來執行各個任務,最後終止這些線程。如果它們是 可接合的線程,且您沒有調用 pthread_join() 來聯結它們,那麼線上程終止後,每個產生的線程都將泄漏大量的記憶體(至少每堆棧
10MB)。隨著建立並在未聯結的情況下終止的背景工作執行緒越來越多,泄漏的記憶體量也持續增加。另外,進程將無法建立新的線程,因為無記憶體可供建立新線程使 用。
清單 1 顯示在忘記聯結可接合線程時引發的嚴重記憶體流失。您還可以使用該代碼檢查可在一個進程空間中共存的線程體的最大量。
#include<stdio.h>#include<pthread.h>void run() { pthread_exit(0);}int main () { pthread_t thread; int rc; long count = 0; while(1) { if(rc = pthread_create(&thread, 0, run, 0) ) { printf("ERROR, rc is %d, so far %ld threads created\n", rc, count); perror("Fail:"); return -1; } count++; } return 0;}
清單 1 中調用了 pthread_create() 來建立一個含預設線程屬性的新線程。預設情況下,新建立的線程是可接合的。它不斷建立新的可接合線程,直至有故障發生。然後輸出錯誤碼和故障原因。
使用以下命令在 Red Hat Enterprise Linux Server 5.4 上編譯清單 1 中的代碼時: [root@server
~]# cc -lpthread thread.c -o thread, 您將獲得清單 2 所示的結果。
清單 2. 記憶體流失結果
[root@server ~]# ./thread ERROR, rc is 12, so far 304 threads created
Fail:: Cannot allocate memory
|
在代碼建立了 304 個線程之後,它無法建立更多線程。錯誤碼是 12,這表示無更多記憶體可用。
如清單 1 和清單 2 所示,雖然產生了可接合線程,但是卻未將其聯結,因此每個終止的可接合線程仍然佔用進程空間,泄漏進程記憶體。
RHEL 上的一個 POSIX 線程擁有一個大小為 10MB 的私人堆棧。換言之,系統為每個 pthread 分配至少 10MB 的專用儲存。在我們的樣本中,304 個線程是在進程停止前建立的;這些線程佔用
304*10MB 記憶體,合計約 3GB。一個進程的虛擬記憶體的大小是 4GB,其中四分之一的進程空間是為 Linux 核心預留的。這樣一來,就有 3GB 的記憶體空間可用作使用者空間。因此,3GB 記憶體由死線程消耗。這是很嚴重的記憶體流失。而且很容易理解它發生的速度為何如此之快。
要修複泄漏,您可以添加代碼調用 pthread_join(),該方法可聯結每個可接合線程。
線程終止的方法:
1)、主線程等待子線程結束 可以用pthread_join函數,pthread_join()函數會等待指定線程的結束,也就是主線程會為子線程阻塞,如果子線程沒有執行完,那麼主線程就永遠不會執行phread_join()下面的程式。程式如下: #include <stdio.h>#include <pthread.h>void pthread_1(){ printf("I am the child!!\n"); sleep(2); printf("I am exit!!\n"); }void main(){ pthread_t tid; void *tret; pthread_create(&tid, NULL, &pthread_1, NULL); pthread_join(tid, &tret); printf("Thread exit with code %d\n", (int)tret); exit(0);} 2)、主線程終止子線程在posix thread中線程有兩中取消狀態:立即取消和延遲取消 立即取消就是pthread_cancel之後,不管理線程在幹什麼,馬上終止這個線程 而延遲取消是在pthread_cancel之後,線程會繼續運行,直到遇到一個 "取消點函數 " 系統預設的是延遲取消如果想要結束線程有幾個方法 a.線程設定為立即取消 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 程式如下#include <stdio.h>#include <unistd.h>#include <pthread.h>#include <sys/types.h>void pthead_1(){ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); while(1) { printf("I am the child \n"); usleep(10000); } }void main(){ pthread_t pthread_id; pthread_create(&pthread_id, NULL, &pthead_1, NULL); sleep(2); pthread_cancel(pthread_id); printf("I make the child exit!!\n"); pthread_exit(0); }b.在你的線程中加入一些取消點函數的調用 while( 1 ) { //sleep(1)或者pthread_testcancle(); }