安裝線程man page,命令:sudo apt-get install manpages-posix-dev
線程概念
什么是線程
LWP:light weight process 輕量級的進程,本質仍是進程(在Linux環境下)
進程:獨立地址空間,擁有PCB
線程:也有PCB,但沒有獨立的地址空間(共享)
區別:在於是否共享地址空間。 獨居(進程);合租(線程)。
Linux下: 線程:最小的執行單位
進程:最小分配資源單位,可看成是只有一個線程的進程。
在程序中創建線程,可以提高效率,進程內線程越多,爭奪到CPU的概率就越大,執行代碼的概率就越大(有一個度)。CPU將線程和進程等同處理
Linux內核線程實現原理
類Unix系統中,早期是沒有“線程”概念的,80年代才引入,借助進程機制實現出了線程的概念。因此在這類系統中,進程和線程關系密切。
1. 輕量級進程(light-weight process),也有PCB,創建線程使用的底層函數和進程一樣,都是clone
2. 從內核里看進程和線程是一樣的,都有各自不同的PCB,但是PCB中指向內存資源的三級頁表是相同的
3. 進程可以蛻變成線程
4. 線程可看做寄存器和棧的集合
5. 在linux下,線程最是小的執行單位;進程是最小的分配資源單位
察看LWP號:ps –Lf pid 查看指定線程的lwp號。ps -eLf
UID PID PPID LWP C NLWP STIME TTY STAT TIME CMD gec 6699 1784 6699 12 54 06:12 ? Sl 0:03 /usr/lib/firefox/firefox gec 6699 1784 6706 0 54 06:12 ? Sl 0:00 /usr/lib/firefox/firefox gec 6699 1784 6707 0 54 06:12 ? Sl 0:00 /usr/lib/firefox/firefox
三級映射:進程PCB --> 頁目錄(可看成數組,首地址位於PCB中) --> 頁表 --> 物理頁面 --> 內存單元
參考:《Linux內核源代碼情景分析》 ----毛德操
對於進程來說,相同的地址(同一個虛擬地址)在不同的進程中,反復使用而不沖突。原因是他們雖虛擬址一樣,但,頁目錄、頁表、物理頁面各不相同。
相同的虛擬址,映射到不同的物理頁面內存單元,最終訪問不同的物理頁面。
但!線程不同!兩個線程具有各自獨立的PCB,但共享同一個頁目錄,也就共享同一個頁表和物理頁面。所以兩個PCB共享一個地址空間。
實際上,無論是創建進程的fork,還是創建線程的pthread_create,底層實現都是調用同一個內核函數clone。
如果復制對方的地址空間,那么就產出一個“進程”;如果共享對方的地址空間,就產生一個“線程”。
因此:Linux內核是不區分進程和線程的。只在用戶層面上進行區分。所以,線程所有操作函數 pthread_* 是庫函數,而非系統調用。
線程共享資源
http://www.cnblogs.com/youxin/p/4102883.html
1.文件描述符表
2.每種信號的處理方式
盡量不要在一個程序中,即使用線程又使用信號,這會比較復雜,不好調試。
線程共享信號處理方式,所以當一個信號來的時候,到底哪一個線程去處理(誰先搶到誰處理)。或者可以單獨設置mask阻塞信號(信號屏蔽字)
3.當前工作目錄
4.用戶ID和組ID
5.內存地址空間 (.text/.data/.bss/heap/共享庫) 。每個線程是唯一的執行單位,因此棧不能共享
線程非共享資源
1.線程id
2.處理器現場和棧指針(內核棧)
3.獨立的棧空間(用戶空間棧)
4.errno變量
5.信號屏蔽字
6.調度優先級
線程優、缺點
優點: 1. 提高程序並發性 2. 開銷小 3. 數據通信、共享數據方便
缺點: 1. 庫函數,不穩定 2. 調試、編寫困難、gdb不支持 3. 對信號支持不好
優點相對突出,缺點均不是硬傷。Linux下由於實現方法導致進程、線程差別不是很大。
線程控制原語
pthread_self函數
獲取線程ID。其作用對應進程中 getpid() 函數。
pthread_t pthread_self(void); 返回值:成功:0; 失敗:無!
線程ID:pthread_t類型,本質:在Linux下為無符號整數(%lu),其他系統中可能是結構體實現
線程ID是進程內部,識別標志。(兩個進程間,線程ID允許相同)
注意:不應使用全局變量 pthread_t tid,在子線程中通過pthread_create傳出參數來獲取線程ID,而應使用pthread_self。
pthread_create函數
創建一個新線程。其作用,對應進程中fork() 函數。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
返回值:成功:0; 失敗:錯誤號 -----Linux環境下,所有線程特點,失敗均直接返回錯誤號。
參數:
pthread_t:當前Linux中可理解為:typedef unsigned long int pthread_t。
1. 傳出參數,保存系統為我們分配好的線程ID
2. 通常傳NULL,表示使用線程默認屬性。若想使用具體屬性也可以修改該參數。
3. 函數指針,指向線程主函數(線程體),該函數運行結束,則線程結束。
4. 線程主函數執行期間所使用的參數(給回調函數傳參)。
在一個線程中調用pthread_create()創建新的線程后,當前線程從pthread_create()返回繼續往下執行,而新的線程所執行的代碼由我們傳給pthread_create的函數指針start_routine決定。start_routine函數接收一個參數,是通過pthread_create的arg參數傳遞給它的,該參數的類型為void *,這個指針按什么類型解釋由調用者自己定義。start_routine的返回值類型也是void *,這個指針的含義同樣由調用者自己定義。start_routine返回時,這個線程就退出了,其它線程可以調用pthread_join得到start_routine的返回值,類似於父進程調用wait(2)得到子進程的退出狀態,稍后詳細介紹pthread_join。
pthread_create成功返回后,新創建的線程的id被填寫到thread參數所指向的內存單元。我們知道進程id的類型是pid_t,每個進程的id在整個系統中是唯一的,調用getpid(2)可以獲得當前進程的id,是一個正整數值。線程id的類型是thread_t,它只在當前進程中保證是唯一的,在不同的系統中thread_t這個類型有不同的實現,它可能是一個整數值,也可能是一個結構體,也可能是一個地址,所以不能簡單地當成整數用printf打印,調用pthread_self(3)可以獲得當前線程的id。
attr參數表示線程屬性,本節不深入討論線程屬性,所有代碼例子都傳NULL給attr參數,表示線程屬性取缺省值,感興趣的讀者可以參考APUE。
【練習】:創建一個新線程,打印線程ID。注意:鏈接線程庫 -lpthread 【pthrd_crt.c】
#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> #include <string.h> void *tfn(void *arg) { printf("tfn-- pid = %d, tid = %lu\n", getpid(), pthread_self()); return (void *)0; } int main(void) { pthread_t tid; printf("main -- pid = %d, tid = %lu\n", getpid(), pthread_self()); int ret = pthread_create(&tid, NULL, tfn, NULL); if (ret != 0) { //printf("pthread_create erro: %s\n", strerror(ret)); fprintf(stderr, "pthread_create erro: %s\n", strerror(ret)); exit(1); } sleep(1); return 0; }
pid = 125776,pthread = 2094708480 pid = 125776,pthread = 2086381312
由於pthread_create的錯誤碼不保存在errno中,因此不能直接用perror(3)打印錯誤信息,可以先用strerror(3)把錯誤碼轉換成錯誤信息再打印。
如果任意一個線程調用了exit或_exit,則整個進程的所有線程都終止,由於從main函數return也相當於調用exit,為了防止新創建的線程還沒有得到執行就終止,
我們在main函數return之前延時1秒,這只是一種權宜之計,即使主線程等待1秒,內核也不一定會調度新創建的線程執行,下一節我們會看到更好的辦法。
【練習】:循環創建多個線程,每個線程打印自己是第幾個被創建的線程。(類似於進程循環創建子進程) 【more_pthrd.c】
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> void *tfn(void *arg) { int i; i = (int)arg; sleep(i); //通過i來區別每個線程 printf("I'm %dth thread, Thread_ID = %lu\n", i+1, pthread_self()); return NULL; } int main(int argc, char *argv[]) { int n = 5, i; pthread_t tid; if (argc == 2) n = atoi(argv[1]); for (i = 0; i < n; i++) { pthread_create(&tid, NULL, tfn, (void *)i); //將i轉換為指針,在tfn中再強轉回整形。 } sleep(n); printf("I am main, and I am not a process, I'm a thread!\n" "main_thread_ID = %lu\n", pthread_self()); pthread_exit(NULL); }
I'm 1th thread, Thread_ID = 140579885291264 I'm 2th thread, Thread_ID = 140579876898560 I'm 3th thread, Thread_ID = 140579868505856 I'm 4th thread, Thread_ID = 140579860113152 I'm 5th thread, Thread_ID = 140579851720448 I am main, and I am not a process, I'm a thread! main_thread_ID = 140579893618432
拓展思考:將pthread_create函數參4修改為(void *)&i, 將線程主函數內改為 i=*((int *)arg) 是否可以。 i++會出現錯亂
線程與共享
【牢記】:線程默認共享數據段、代碼段等地址空間,常用的是全局變量。而進程不共享全局變量,只能借助mmap。
【練習】:設計程序,驗證線程之間共享全局數據。【glb_var_pthrd.c】
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> int var = 100; void *tfn(void *arg) { var = 200; printf("thread\n"); return NULL; } int main(void) { printf("At first var = %d\n", var); pthread_t tid; pthread_create(&tid, NULL, tfn, NULL); sleep(1); printf("after pthread_create, var = %d\n", var); return 0; }
At first var = 100 thread after pthread_create, var = 200
pthread_exit函數
將單個線程退出
void pthread_exit(void *retval); 參數:retval表示線程退出狀態,通常傳NULL
思考:使用exit將指定線程退出,可以嗎?【pthrd_exit.c】
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> void *tfn(void *arg) { int i; i = (int)arg;//強轉。 if (i == 2) pthread_exit(NULL); sleep(i); //通過i來區別每個線程 printf("I'm %dth thread, Thread_ID = %lu\n", i+1, pthread_self()); return NULL; } int main(int argc, char *argv[]) { int n = 5, i; pthread_t tid; if (argc == 2) n = atoi(argv[1]); for (i = 0; i < n; i++) { pthread_create(&tid, NULL, tfn, (void *)i); //將i轉換為指針,在tfn中再強轉回整形。 } sleep(n); printf("I am main, I'm a thread!\n" "main_thread_ID = %lu\n", pthread_self()); return 0; }
結論:線程中,禁止使用exit函數,會導致進程內所有線程全部退出。
在不添加sleep控制輸出順序的情況下。pthread_create在循環中,幾乎瞬間創建5個線程,但只有第1個線程有機會輸出(或者第2個也有,也可能沒有,取決於內核調度)如果第3個線程執行了exit,將整個進程退出了,所以全部線程退出了。
所以,多線程環境中,應盡量少用,或者不使用exit函數,取而代之使用pthread_exit函數,將單個線程退出。任何線程里exit導致進程退出,其他線程未工作結束,主控線程退出時不能return或exit。
另注意,pthread_exit或者return返回的指針所指向的內存單元必須是全局的或者是用malloc分配的,不能在線程函數的棧上分配,因為當其它線程得到這個返回指針時線程函數已經退出了。
【練習】:編寫多線程程序,總結exit、return、pthread_exit各自退出效果。
return:返回到調用者那里去。
pthread_exit():將調用該函數的線程退出
exit: 將進程退出。
pthread_join函數
阻塞等待線程退出,獲取線程退出狀態 其作用,對應進程中 waitpid() 函數。
int pthread_join(pthread_t thread, void **retval); 成功:0;失敗:錯誤號
參數:thread:線程ID (【注意】:不是指針);retval:存儲線程結束狀態。
對比記憶:
進程中:main返回值、exit參數-->int;等待子進程結束 wait 函數參數-->int *
線程中:線程主函數返回值、pthread_exit-->void *;等待線程結束 pthread_join 函數參數-->void **
【練習】:參數 retval 非空用法。【pthrd_exit_join.c】
#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> typedef struct{ int a; int b; } exit_t; void *tfn(void *arg) { exit_t *ret; ret = malloc(sizeof(exit_t)); ret->a = 100; ret->b = 300; pthread_exit((void *)ret); return NULL; //should not be here. } int main(void) { pthread_t tid; exit_t *retval; pthread_create(&tid, NULL, tfn, NULL); /*調用pthread_join可以獲取線程的退出狀態*/ pthread_join(tid, (void **)&retval); printf("a = %d, b = %d \n", retval->a, retval->b); return 0; }
調用該函數的線程將掛起等待,直到id為thread的線程終止。thread線程以不同的方法終止,通過pthread_join得到的終止狀態是不同的,總結如下:
1. 如果thread線程通過return返回,retval所指向的單元里存放的是thread線程函數的返回值。
2. 如果thread線程被別的線程調用pthread_cancel異常終止掉,retval所指向的單元里存放的是常數PTHREAD_CANCELED。
3. 如果thread線程是自己調用pthread_exit終止的,retval所指向的單元存放的是傳給pthread_exit的參數。
4. 如果對thread線程的終止狀態不感興趣,可以傳NULL給retval參數。
【練習】:使用pthread_join函數將循環創建的多個子線程回收。【pthrd_loop_join.c】

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int var = 100; void *tfn(void *arg) { int i; i = (int)arg; sleep(i); if (i == 1) { //i = 0 100 333 333 777 777 var = 333; printf("var = %d\n", var); pthread_exit((void *)var); } else if (i == 3) { var = 777; printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i+1, pthread_self(), var); pthread_exit((void *)var); } else { printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i+1, pthread_self(), var); pthread_exit((void *)var); } return NULL; } int main(void) { pthread_t tid[5]; int i, *ret[5]; for (i = 0; i < 5; i++) pthread_create(&tid[i], NULL, tfn, (void *)i); for (i = 0; i < 5; i++) { pthread_join(tid[i], (void **)&ret[i]); printf("-------%d 's ret = %d\n", i, (int)ret[i]); } printf("I'm main pthread tid = %lu\t var = %d\n", pthread_self(), var); pthread_exit(NULL); }
pthread_detach函數
實現線程分離
int pthread_detach(pthread_t thread); 成功:0;失敗:錯誤號
線程分離狀態:指定該狀態,線程主動與主控線程斷開關系。線程結束后,其退出狀態不由其他線程獲取,而直接自己自動釋放。網絡、多線程服務器常用。
進程若有該機制,將不會產生僵屍進程。僵屍進程的產生主要由於進程死后,大部分資源被釋放,一點殘留資源仍存於系統中,導致內核認為該進程仍存在。
也可使用 pthread_create函數參2(線程屬性)來設置線程分離。
【練習】:使用pthread_detach函數實現線程分離 【pthrd_detach.c】

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <string.h> void *tfn(void *arg) { int n = 3; while (n--) { printf("thread count %d\n", n); sleep(1); } return (void *)1; } int main(void) { pthread_t tid; void *tret; int err; #if 1 pthread_attr_t attr; /*通過線程屬性來設置游離態*/ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&tid, &attr, tfn, NULL); pthread_attr_destroy(&attr); #else pthread_create(&tid, NULL, tfn, NULL); pthread_detach(tid); #endif while (1) { err = pthread_join(tid, &tret); if (err != 0) fprintf(stderr, "thread %s\n", strerror(err)); else fprintf(stderr, "thread exit code %d\n", (int)tret); sleep(1); } return 0; }
一般情況下,線程終止后,其終止狀態一直保留到其它線程調用pthread_join獲取它的狀態為止。
但是線程也可以被置為detach狀態,這樣的線程一旦終止就立刻回收它占用的所有資源,而不保留終止狀態。
不能對一個已經處於detach狀態的線程調用pthread_join,這樣的調用將返回EINVAL。如果已經對一個線程調用了pthread_detach就不能再調用pthread_join了。
pthread_cancel函數
殺死(取消)線程 其作用,對應進程中 kill() 函數。
int pthread_cancel(pthread_t thread); 成功:0;失敗:錯誤號
【注意】:線程的取消並不是實時的,而又一定的延時。需要等待線程到達某個取消點(檢查點)。
類似於玩游戲存檔,必須到達指定的場所(存檔點,如:客棧、倉庫、城里等)才能存儲進度。殺死線程也不是立刻就能完成,必須要到達取消點。
取消點:是線程檢查是否被取消,並按請求進行動作的一個位置。通常是一些系統調用creat,open,pause,close,read,write..... ——參 APUE.12.7 取消選項。
可粗略認為一個系統調用(進入內核)即為一個取消點。
被取消的線程,退出值,定義在Linux的pthread庫中常數PTHREAD_CANCELED的值是-1。
可以在頭文件pthread.h中找到它的定義:#define PTHREAD_CANCELED ((void *) -1)
【練習】:終止線程的三種方法。注意“取消點”的概念。【pthrd_endof3.c】
#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> void *tfn1(void *arg) { printf("thread 1 returning\n"); return (void *)111; } void *tfn2(void *arg) { printf("thread 2 exiting\n"); pthread_exit((void *)222); } void *tfn3(void *arg) { while (1) { // printf("thread 3: I'm going to die 1 seconds after...\n"); // sleep(1); pthread_testcancel(); //自己添加取消點 } } int main(void) { pthread_t tid; void *tret = NULL; pthread_create(&tid, NULL, tfn1, NULL); pthread_join(tid, &tret); printf("thread 1 exit code = %d\n\n", (int)tret); pthread_create(&tid, NULL, tfn2, NULL); pthread_join(tid, &tret); printf("thread 2 exit code = %d\n\n", (int)tret); pthread_create(&tid, NULL, tfn3, NULL); sleep(3); pthread_cancel(tid); pthread_join(tid, &tret); printf("thread 3 exit code = %d\n", (int)tret); return 0; }
終止線程方式
總結:終止某個線程而不終止整個進程,有三種方法:
1. 從線程主函數return。這種方法對主控線程不適用,從main函數return相當於調用exit。
2. 一個線程可以調用pthread_cancel終止同一進程中的另一個線程。
3. 線程可以調用pthread_exit終止自己。
pthread_equal函數
比較兩個線程ID是否相等。
int pthread_equal(pthread_t t1, pthread_t t2);
有可能Linux在未來線程ID pthread_t 類型被修改為結構體實現。
控制原語對比
進程 線程
fork pthread_create
exit pthread_exit
wait pthread_join
kill pthread_cancel
getpid pthread_self 命名空間
線程屬性
本節作為指引性介紹,linux下線程的屬性是可以根據實際項目需要,進行設置,之前我們討論的線程都是采用線程的默認屬性,默認屬性已經可以解決絕大多數開發時遇到的問題。
如我們對程序的性能提出更高的要求那么需要設置線程屬性,比如可以通過設置線程棧的大小來降低內存的使用,增加最大線程個數。
typedef struct { int etachstate; //線程的分離狀態 int schedpolicy; //線程調度策略 struct sched_param schedparam; //線程的調度參數 int inheritsched; //線程的繼承性 int scope; //線程的作用域 size_t guardsize; //線程棧末尾的警戒緩沖區大小 int stackaddr_set; //線程的棧設置 void* stackaddr; //線程棧的位置 size_t stacksize; //線程棧的大小 } pthread_attr_t;
主要結構體成員:
1. 線程分離狀態
2. 線程棧大小(默認平均分配)
3. 線程棧警戒緩沖區大小(位於棧末尾) 參 APUE.12.3 線程屬性
4. 線程棧最低地址
屬性值不能直接設置,須使用相關函數進行操作,初始化的函數為pthread_attr_init,這個函數必須在pthread_create函數之前調用。之后須用pthread_attr_destroy函數來釋放資源。
線程屬性主要包括如下屬性:作用域(scope)、棧尺寸(stack size)、棧地址(stack address)、優先級(priority)、分離的狀態(detached state)、調度策略和參數(scheduling policy and parameters)。
默認的屬性為非綁定、非分離、缺省的堆棧、與父進程同樣級別的優先級。
線程屬性初始化
注意:應先初始化線程屬性,再pthread_create創建線程
初始化線程屬性
int pthread_attr_init(pthread_attr_t *attr); 成功:0;失敗:錯誤號
銷毀線程屬性所占用的資源
int pthread_attr_destroy(pthread_attr_t *attr); 成功:0;失敗:錯誤號
線程的分離狀態
線程的分離狀態決定一個線程以什么樣的方式來終止自己。
非分離狀態:線程的默認屬性是非分離狀態,這種情況下,原有的線程等待創建的線程結束。只有當pthread_join()函數返回時,創建的線程才算終止,才能釋放自己占用的系統資源。
分離狀態:分離線程沒有被其他的線程所等待,自己運行結束了,線程也就終止了,馬上釋放系統資源。應該根據自己的需要,選擇適當的分離狀態。
線程分離狀態的函數:
設置線程屬性,分離or非分離
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
獲取程屬性,分離or非分離
pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
參數: attr:已初始化的線程屬性
detachstate: PTHREAD_CREATE_DETACHED(分離線程)
PTHREAD _CREATE_JOINABLE(非分離線程)
這里要注意的一點是,如果設置一個線程為分離線程,而這個線程運行又非常快,它很可能在pthread_create函數返回之前就終止了,它終止以后就可能將線程號和系統資源移交給其他的線程使用,
這樣調用pthread_create的線程就得到了錯誤的線程號。要避免這種情況可以采取一定的同步措施,最簡單的方法之一是可以在被創建的線程里調用pthread_cond_timedwait函數,
讓這個線程等待一會兒,留出足夠的時間讓函數pthread_create返回。設置一段等待時間,是在多線程編程里常用的方法。但是注意不要使用諸如wait()之類的函數,它們是使整個進程睡眠,並不能解決線程同步的問題。
線程的棧地址
POSIX.1定義了兩個常量_POSIX_THREAD_ATTR_STACKADDR 和_POSIX_THREAD_ATTR_STACKSIZE檢測系統是否支持棧屬性。
也可以給sysconf函數傳遞_SC_THREAD_ATTR_STACKADDR或 _SC_THREAD_ATTR_STACKSIZE來進行檢測。
當進程棧地址空間不夠用時,指定新建線程使用由malloc分配的空間作為自己的棧空間。通過pthread_attr_setstack和pthread_attr_getstack兩個函數分別設置和獲取線程的棧地址。
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); 成功:0;失敗:錯誤號
int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize); 成功:0;失敗:錯誤號
參數: attr:指向一個線程屬性的指針
stackaddr:返回獲取的棧地址
stacksize:返回獲取的棧大小
線程的棧大小
當系統中有很多線程時,可能需要減小每個線程棧的默認大小,防止進程的地址空間不夠用,當線程調用的函數會分配很大的局部變量或者函數調用層次很深時,可能需要增大線程棧的默認大小。
函數pthread_attr_getstacksize和 pthread_attr_setstacksize提供設置。
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); 成功:0;失敗:錯誤號
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize); 成功:0;失敗:錯誤號
參數: attr:指向一個線程屬性的指針
stacksize:返回線程的堆棧大小
線程屬性控制示例
#include <stdio.h> #include <pthread.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define SIZE 0x10 void *th_fun(void *arg) { while (1) sleep(1); } int main(void) { pthread_t tid; int err, detachstate, i = 1; pthread_attr_t attr; size_t stacksize; void *stackaddr; pthread_attr_init(&attr); pthread_attr_getstack(&attr, &stackaddr, &stacksize); pthread_attr_getdetachstate(&attr, &detachstate); if (detachstate == PTHREAD_CREATE_DETACHED) printf("thread detached\n"); else if (detachstate == PTHREAD_CREATE_JOINABLE) printf("thread join\n"); else printf("thread un known\n"); /* 設置線程分離屬性 */ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); while (1) { /* 在堆上申請內存,指定線程棧的起始地址和大小 */ stackaddr = malloc(SIZE); if (stackaddr == NULL) { perror("malloc"); exit(1); } stacksize = SIZE; pthread_attr_setstack(&attr, stackaddr, stacksize); err = pthread_create(&tid, &attr, th_fun, NULL); if (err != 0) { printf("%s\n", strerror(err)); exit(1); } printf("%d\n", i++); } pthread_attr_destroy(&attr); return 0; }
NPTL
1.察看當前pthread庫版本getconf GNU_LIBPTHREAD_VERSION
2.NPTL實現機制(POSIX),Native POSIX Thread Library
3.使用線程庫時gcc指定 –lpthread
線程使用注意事項
1. 主線程退出其他線程不退出,主線程應調用pthread_exit
2. 避免僵屍線程
pthread_join
pthread_detach
pthread_create指定分離屬性
被join線程可能在join函數返回前就釋放完自己的所有內存資源,所以不應當返回被回收線程棧中的值;
3. malloc和mmap申請的內存可以被其他線程釋放
4. 應避免在多線程模型中調用fork除非,馬上exec,子進程中只有調用fork的線程存在,其他線程在子進程中均pthread_exit
5. 信號的復雜語義很難和多線程共存,應避免在多線程引入信號機制