Linux pthread_creat() 建立執行緒失敗問題總結
Linux pthread_creat() 建立執行緒失敗問題總結
目錄
1)更改執行緒執行函式,增加2秒延時,則可建立的執行緒數將減少
-
問題場景
發現建立執行緒時建立失敗,因當時沒有增加失敗時的錯誤碼列印,故無法確認當時的錯誤原因
-
問題詳細描述
發現建立執行緒時建立失敗,因當時沒有增加失敗時的錯誤碼列印,故無法確認當時的錯誤原因
-
問題分析定位
1)pthread_create() 函式原型
NAME pthread_create - create a new thread SYNOPSIS #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); Compile and link with -pthread. DESCRIPTION The pthread_create() function starts a new thread in the calling process. The new thread starts execution by invoking start_routine(); arg is passed as the sole argument of start_routine(). RETURN VALUE On success, pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined. ERRORS EAGAIN Insufficient resources to create another thread, or a system- imposed limit on the number of threads was encountered. The latter case may occur in two ways: the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), which limits the number of process for a real user ID, was reached; or the kernel's sys‐ tem-wide limit on the number of threads, /proc/sys/ker‐ nel/threads-max, was reached. EINVAL Invalid settings in attr. EPERM No permission to set the scheduling policy and parameters speci‐fied in attr.
從如上可知,執行緒建立失敗的原因有3點:
- EAGAIN 建立執行緒時,資源數不足,或者是遇到了系統對執行緒數量的限制
- EINVAL arrt 中使用了無效引數
- EPERM 系統不允許設定排程策略 和 在attr中使用特定的引數
根據實際情況,大概建立執行緒時,資源不足原因較為匹配;
2)實測系統最多可建立的執行緒數
執行緒數測試程式如下:
1 #include <pthread.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5
6 void *thread_func(void *arg)
7 {
8 static int count = 1;
9 int *p_tmp = arg;
10 int data[10240] = {0};
// pthread_detach(pthread_self()); // 在字執行緒中呼叫,設定自己為分離狀態
11
12 memset(data, 15, sizeof(data));
13 printf("now create thread is %d, *p_tmp = %d\n", count, *p_tmp);
14 // pthread_detach(pthread_self());
15
16 usleep(100);
17 count++;
18 }
19
20 int main(void)
21 {
22 int err = 0;
23 int arg = 100;
24 pthread_t tid;
25 while(1)
26 {
27 err=pthread_create(&tid, NULL, thread_func, &arg);
28 if (0 != err)
29 {
30 printf("can't creat thread: %s\n", strerror(err));
31 break;
32 }
33 usleep(300);
34 }
35 printf("func = [%s] leave.\n", __func__);
36 return 0;
37 }
38
39
3)測試結果
當前作業系統市64位,4核,實際建立了 32754 個執行緒;
[email protected]:/home# getconf LONG_BIT
64
now create thread is 32750, *p_tmp = 100
now create thread is 32751, *p_tmp = 100
now create thread is 32752, *p_tmp = 100
now create thread is 32753, *p_tmp = 100
now create thread is 32754, *p_tmp = 100
can't creat thread: Resource temporarily unavailable
func = [main] leave.
4)檢視shell啟動程序所佔用的資源預設設定:
執行緒的棧大小為8M ;
[email protected]:/home# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 62963
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192 //執行緒的棧大小 8M
cpu time (seconds, -t) unlimited
max user processes (-u) 62963
virtual memory (kbytes, -v) unlimited //虛擬記憶體沒有限制
file locks (-x) unlimited
5)確認系統可建立的最大執行緒數:
1) 在/usr/include/bits/local_lim.h下,有的是#define PTHREAD_THREADS_MAX 1024,可見最大執行緒數限制為1024;
而有的沒有限制,比如我的PC機/include/x86_64-linux-gnu/bits/local_lim.h:
建立執行緒的最大數目,也受限於系統資源,主要是執行緒的stack所佔用的記憶體,可用命令ulimits -s檢視,一般是8192KB(8M)。
[email protected]:/home# ulimit -s
8192
2) 查系統支援的最大執行緒數,一般會很大,相當於理論值
[email protected]:/home# cat /proc/sys/kernel/threads-max
125926
-
問題根因
can't creat thread: Resource temporarily unavailable,
初步判斷,因系統資源不足,建立執行緒失敗;
-
解決方案
1)建立執行緒時,一定要回收執行緒的資源;
2)不能頻繁建立執行緒,確保若當前系統有執行緒正在執行時,不能再建立執行緒,待當前執行緒執行結束後,方可建立;
-
回收執行緒資源
1)建立的執行緒狀態有兩種
linux執行緒執行和windows不同,pthread_create() 建立的執行緒有兩種狀態joinable狀態和unjoinable狀態:
A. 如果執行緒是joinable狀態,當執行緒執行函式自己返回退出時 或 pthread_exit時都不會釋放執行緒所佔用堆疊和執行緒描述符(總計8K多) 。只有當呼叫了pthread_join() 之後這些資源才會被釋放。
B. 若是unjoinable狀態的執行緒,這些資源線上程函式退出時 或 pthread_exit時自動會被釋放。
unjoinable屬性可以在pthread_create() 時指定,或線上程建立後線上程中pthread_detach自己, 如:pthread_detach(pthread_self()),將狀態改為unjoinable狀態,確保資源的釋放。或者將執行緒置為 joinable,然後適時呼叫pthread_join.
綜上所述:
在 子執行緒執行函式里加上 pthread_detach(pthread_self())的話,執行緒狀態就會改變為分離狀態;
當伺服器程式長期執行,長時間執行緒的建立,執行緒資源的回收就是一個問題:
Linux系統中程式的執行緒資源是有限的,表現為對於一個程式其能同時執行的執行緒數是有限的。而預設的條件下,一個執行緒結束後,其對應的資源不會被釋放;所以如果在一個程式中,反覆建立執行緒,而執行緒又預設的退出,則最終執行緒資源耗盡,程序將不再能建立新的執行緒。
解決這個問題,有2種方法:a. 系統自動釋放執行緒資源; b. 由另一個執行緒釋放該執行緒資源。
程序執行後,本身也是一個執行緒——主執行緒,主執行緒和主執行緒建立的執行緒共享程序資源。不同於其他執行緒,在於主執行緒執行結束後,程式退出,所有程式建立的執行緒也會退出。
2)系統自動釋放執行緒資源
如果想線上程結束時,由系統釋放執行緒資源,則要設定執行緒屬性為detach,使執行緒分離主執行緒,程式碼上,可以這樣表示:
pthread_t t;
pthread_attr_t a; //執行緒屬性
pthread_attr_init(&a); //初始化執行緒屬性
pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); //設定執行緒屬性
pthread_create( &t, &a, thread_func, (void*)lp); //建立執行緒
3)由另一個執行緒釋放該資源
程式碼上,可以這樣表示:
pthread_t t;
pthread_create( &t, NULL, thread_func , (void*)lp);
pthread_join( t);
pthread_join( t),等待執行緒t退出,並釋放 t執行緒所佔用的資源。
pthread_join函式會阻塞等待指定執行緒退出,然後回收資源,這樣就有同步的功能,使一個執行緒等待另一個執行緒退出,然後才繼續執行;
缺點:但是對於伺服器程式如果主執行緒在新建立的執行緒工作時還需要做別的事情,這種方法不是很好,就需要使用系統自動釋放。
4)如下2個函式,也可改變執行緒的狀態為分離態:
a. 在 子執行緒中 呼叫pthread_detach(pthread_self());
b. 在 主執行緒中 呼叫pthread_detach(pid),pid為子執行緒的執行緒號
5)實測改為分離態後可建立的執行緒數目
使用pthread_detach(pthread_self())設定建立的執行緒為分離狀態,測試結果如下:
當設定字執行緒為分離狀態以後,可以建立無數個執行緒,如下建立了69023個執行緒,還沒有失敗;
6 void *thread_func(void *arg)
7 {
8 static int count = 1;
9 int *p_tmp = arg;
10 // int data[10240] = {0};
11
12 pthread_detach(pthread_self());
13 // memset(data, 15, sizeof(data));
14 printf("now create thread is %d, *p_tmp = %d\n", count, *p_tmp);
15 // pthread_detach(pthread_self());
16 sleep(10);
17 usleep(100);
18 count++;
19 }
測試結果:
若不手動停止執行緒建立,可以一直建立。
now create thread is 69018, *p_tmp = 100
now create thread is 69019, *p_tmp = 100
now create thread is 69021, *p_tmp = 100
now create thread is 69021, *p_tmp = 100
now create thread is 69022, *p_tmp = 100
now create thread is 69023, *p_tmp = 100
-
總結
綜上所述:
1)計算可建立的最大執行緒數公式
理論上linux 上最大執行緒數是 = 總虛擬記憶體(使用者空間) / 執行緒棧大小;
一般32bit PC機系統上,程序空間是4G,其中0——3G 是使用者空間,3G ——4G 是核心空間,所以理論上最大執行緒數 = 3*1024/ 8M = 384個,考慮系統主執行緒佔用情況,故可建立的最大執行緒大概為 < 384個;
2)檢視linux 系統虛擬記憶體大小
我的pc 機,使用者空間虛擬記憶體為 unlimit,故可建立的執行緒數以實際為準:
[email protected]:/home# ulimit -v
unlimited
3)ulimit 常用命令:
檢查虛擬記憶體: ulimit -v
檢查棧大小: ulimit -s
設定虛擬記憶體: ulimit -v 新值
設定棧大小: ulimit -s 新值
或者pthread_create用pthread_attr_getstacksize設定一個較小的棧大小
4)使執行緒資料增加的方法
a. 通過設定執行緒狀態為主分離態,可使得執行緒執行結束,自動回收執行緒資源,則系統可無限制的建立執行緒。
b. 可通過減小棧限制或增大虛擬記憶體使得執行緒的數目增加。
c. 程序最多可以建立的執行緒數是根據分配給呼叫棧的大小,以及作業系統(32位和64位不同)共同決定的。
-
附件
1)更改執行緒執行函式,增加2秒延時,則可建立的執行緒數將減少
因若字執行緒執行函式,增加了延時,那麼就會導致建立的執行緒多佔用系統資源2秒的時間,則系統資源數不足,建立的執行緒也就減少。
6 void *thread_func(void *arg)
7 {
8 static int count = 1;
9 int *p_tmp = arg;
10 int data[10240] = {0};
11
12 memset(data, 15, sizeof(data));
13 printf("now create thread is %d, *p_tmp = %d\n", count, *p_tmp);
14 // pthread_detach(pthread_self());
15 sleep(2);
16 usleep(100);
17 count++;
18 }
2)測試結果
實際可建立的執行緒為 27781個;
now create thread is 27776, *p_tmp = 100
now create thread is 27776, *p_tmp = 100
now create thread is 27778, *p_tmp = 100
now create thread is 27779, *p_tmp = 100
now create thread is 27780, *p_tmp = 100
now create thread is 27781, *p_tmp = 100
can't creat thread: Resource temporarily unavailable
func = [main] leave.