Linux pthread_creat() 创建线程失败问题总结
目录
问题场景
问题详细描述
问题分析定位
1)pthread_create() 函数原型
2)实测系统最多可创建的线程数
3)测试结果
4)查看shell启动进程所占用的资源默认设置:
5)确认系统可创建的最大线程数:
问题根因
解决方案
回收线程资源
1)创建的线程状态有两种
2)系统自动释放线程资源
3)由另一个线程释放该资源
4)如下2个函数,也可改变线程的状态为分离态:
5)实测改为分离态后可创建的线程数目
总结
1)计算可创建的最大线程数公式
2)查看linux 系统虚拟内存大小
3)ulimit 常用命令:
4)使线程数据增加的方法
附件
1)更改线程执行函数,增加2秒延时,则可创建的线程数将减少
2)测试结果
发现创建线程时创建失败,因当时没有增加失败时的错误码打印,故无法确认当时的错误原因
发现创建线程时创建失败,因当时没有增加失败时的错误码打印,故无法确认当时的错误原因
NAME
pthread_create - create a new thread
SYNOPSIS
#include
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点:
根据实际情况,大概创建线程时,资源不足原因较为匹配;
线程数测试程序如下:
1 #include
2 #include
3 #include
4 #include
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
当前操作系统市64位,4核,实际创建了 32754 个线程;
root@zll-Lenovo:/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.
线程的栈大小为8M ;
root@zll-Lenovo:/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
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)。
root@zll-Lenovo:/home# ulimit -s
8192
2) 查系统支持的最大线程数,一般会很大,相当于理论值
root@zll-Lenovo:/home# cat /proc/sys/kernel/threads-max
125926
can't creat thread: Resource temporarily unavailable,
初步判断,因系统资源不足,创建线程失败;
1)创建线程时,一定要回收线程的资源;
2)不能频繁创建线程,确保若当前系统有线程正在执行时,不能再创建线程,待当前线程执行结束后,方可创建;
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. 由另一个线程释放该线程资源。
进程运行后,本身也是一个线程——主线程,主线程和主线程建立的线程共享进程资源。不同于其他线程,在于主线程运行结束后,程序退出,所有程序建立的线程也会退出。
如果想在线程结束时,由系统释放线程资源,则要设置线程属性为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); //建立线程
代码上,可以这样表示:
pthread_t t;
pthread_create( &t, NULL, thread_func , (void*)lp);
pthread_join( t);
pthread_join( t),等待线程t退出,并释放 t线程所占用的资源。
pthread_join函数会阻塞等待指定线程退出,然后回收资源,这样就有同步的功能,使一个线程等待另一个线程退出,然后才继续运行;
缺点:但是对于服务器程序如果主线程在新创建的线程工作时还需要做别的事情,这种方法不是很好,就需要使用系统自动释放。
a. 在 子线程中 调用pthread_detach(pthread_self());
b. 在 主线程中 调用pthread_detach(pid),pid为子线程的线程号
使用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
综上所述:
理论上linux 上最大线程数是 = 总虚拟内存(用户空间) / 线程栈大小;
一般32bit PC机系统上,进程空间是4G,其中0——3G 是用户空间,3G ——4G 是内核空间,所以理论上最大线程数 = 3*1024/ 8M = 384个,考虑系统主线程占用情况,故可创建的最大线程大概为 < 384个;
我的pc 机,用户空间虚拟内存为 unlimit,故可创建的线程数以实际为准:
root@zll-Lenovo:/home# ulimit -v
unlimited
检查虚拟内存: ulimit -v
检查栈大小: ulimit -s
设置虚拟内存: ulimit -v 新值
设置栈大小: ulimit -s 新值
或者pthread_create用pthread_attr_getstacksize设置一个较小的栈大小
a. 通过设置线程状态为主分离态,可使得线程执行结束,自动回收线程资源,则系统可无限制的创建线程。
b. 可通过减小栈限制或增大虚拟内存使得线程的数目增加。
c. 进程最多可以创建的线程数是根据分配给调用栈的大小,以及操作系统(32位和64位不同)共同决定的。
因若字线程执行函数,增加了延时,那么就会导致创建的线程多占用系统资源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 }
实际可创建的线程为 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.