Linux pthread_creat() 创建线程失败问题总结

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)测试结果


  • 问题场景

发现创建线程时创建失败,因当时没有增加失败时的错误码打印,故无法确认当时的错误原因

  • 问题详细描述

发现创建线程时创建失败,因当时没有增加失败时的错误码打印,故无法确认当时的错误原因

  • 问题分析定位

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点:

  1. EAGAIN  创建线程时,资源数不足,或者是遇到了系统对线程数量的限制
  2. EINVAL   arrt 中使用了无效参数
  3. 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 个线程;
 

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.

4)查看shell启动进程所占用的资源默认设置:

线程的栈大小为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

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)。

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)不能频繁创建线程,确保若当前系统有线程正在执行时,不能再创建线程,待当前线程执行结束后,方可创建;

  • 回收线程资源

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,故可创建的线程数以实际为准:

root@zll-Lenovo:/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.

 

  • 9
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
通过pthread_create和pthread_exit调用参数与线程T2交换数据的方法如下: 1. 首先,在主线程创建线程T2和线程T1,通过pthread_create函数传递参数给线程T2。例如,可以通过定义一个结构体来传递需要交换的数据,然后将该结构体指针作为参数传递给pthread_create函数。 2. 在线程T2中,通过解引用参数指针来获取到传递的数据。可以使用类型转换将void类型的参数指针转换为所需的类型,然后再进行解引用操作。 3. 在线程T2中,通过修改数据来与线程T1交换数据。可以通过指针操作来修改数据内容。 4. 线程T2执行完毕后,可以通过pthread_exit函数退出线程,并将需要传递给主线程的数据作为参数传递给pthread_exit函数。 5. 在主线程中,可以通过pthread_join函数获取线程T2的退出状态,并通过解引用参数指针来获取到传递给pthread_exit函数的数据。 总结起来,通过pthread_create函数传递参数给线程T2,线程T2通过解引用参数指针获取到传递的数据,并通过修改数据来与线程T1交换数据。线程T2执行完毕后,通过pthread_exit函数退出线程,并将需要传递给主线程的数据作为参数传递给pthread_exit函数。在主线程中,可以通过pthread_join函数获取线程T2的退出状态,并通过解引用参数指针来获取到传递给pthread_exit函数的数据。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Linux线程(2)——创建、终止和回收(pthread_create()、pthread_exit()、pthread_join())](https://blog.csdn.net/cj_lsk/article/details/130229709)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Linux线程编程(pthread_creat、pthread_exit、pthread_join、pthread_detach)](https://blog.csdn.net/weixin_42934918/article/details/114363789)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值