NPTL将最大线程数限制在65528个?

5
以下代码应该会创建100,000个线程:
/* compile with:   gcc -lpthread -o thread-limit thread-limit.c */
/* originally from: http://www.volano.com/linuxnotes.html */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

#define MAX_THREADS 100000
int i;

void run(void) {
  sleep(60 * 60);
}

int main(int argc, char *argv[]) {
  int rc = 0;
  pthread_t thread[MAX_THREADS];
  printf("Creating threads ...\n");
  for (i = 0; i < MAX_THREADS && rc == 0; i++) {
    rc = pthread_create(&(thread[i]), NULL, (void *) &run, NULL);
    if (rc == 0) {
      pthread_detach(thread[i]);
      if ((i + 1) % 100 == 0)
    printf("%i threads so far ...\n", i + 1);
    }
    else
    {
      printf("Failed with return code %i creating thread %i (%s).\n",
         rc, i + 1, strerror(rc));

      // can we allocate memory?
      char *block = NULL;
      block = malloc(65545);
      if(block == NULL)
        printf("Malloc failed too :( \n");
      else
        printf("Malloc worked, hmmm\n");
    }
  }
sleep(60*60); // ctrl+c to exit; makes it easier to see mem use
  exit(0);
}

这是在一台64位机器上运行,内存为32GB; 安装了Debian 5.0,全部为默认设置。
  • ulimit -s 512 以保持堆栈大小
  • /proc/sys/kernel/pid_max 设置为1,000,000(默认情况下,它限制为32k个进程ID)。
  • ulimit -u 1000000 增加最大进程数(认为这没有什么影响)
  • /proc/sys/kernel/threads-max 设置为1,000,000(默认情况下未设置)

运行此命令会输出以下内容:

65500 threads so far ...
Failed with return code 12 creating thread 65529 (Cannot allocate memory).
Malloc worked, hmmm

我肯定不会用尽内存;我甚至可以启动几个这些程序,同时运行它们的65k线程。

(请不要建议我不要尝试启动100,000+线程。这只是对某些东西进行简单测试,应该可以工作。我的当前基于epoll的服务器始终具有大约200k+连接,各种论文表明线程可能是更好的选择。- 谢谢 :))


是的,我已经尝试将其设置为简单的ulimit -s 1,结果65528个线程是相同的。如果我使用ulimit -s 1024也是一样的。 - rekamso
1
你能通过strace(和耐心)确认最后的pthread_create(clone(2)?)调用是否实际上以ENOMEM失败吗?如果增加/ proc / sys /文件的值:vm / max_map_count,kernel / pid_max和kernel / threads-max会发生什么? - pilcrow
3
你的“各种论文”链接只指向一篇论文,它基本上说,如果你将线程库替换为自定义的绿色线程实现,或许还要更改编译器,那么线程是很好的。但是,你的测试代码使用的是原始编译器和操作系统线程,所以即使你选择这篇论文来支持你尝试这个方法的决定,我也不明白原因。此外,该论文忽略了线程在本质上是不确定性的这一事实。你应该阅读这位伯克利大学的其他人的新论文:http://www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf - Warren Young
你可以考虑采用混合方法。如Loan所说,许多线程可能会引起巨大的上下文切换开销问题,同时由于你所提到的小栈,很容易越过栈的顶部,这是产生Heisenbugs的一种方式。此外,根据pthread_attr_setstack()手册页的说明,你可以使用的最小堆栈大小为16k。 - Spudd86
线程是更好的选择,但最多只需要几十个,而不是数十万个!您只需要与同时需要执行的任务一样多的线程。 - David Schwartz
显示剩余3条评论
4个回答

6

pilcrow提到的/proc/sys/vm/max_map_count是正确的做法;增加这个值可以允许打开更多的线程;虽然我不确定涉及的确切公式是什么,但是1百万以上的值可以允许开启约30万个线程。

(对于任何试图使用10万个以上的线程的人,请查看pthread_create的mmap问题...当低内存用完时,创建新线程会变得非常慢。)


0

这可能有助于将程序中的堆栈大小设置为最小值(如果不够,您可以选择):

/* compile with:   gcc -lpthread -o thread-limit thread-limit.c */
/* originally from: http://www.volano.com/linuxnotes.html */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

#define MAX_THREADS 100000
int i;

void run(void) {
  sleep(60 * 60);
}

int main(int argc, char *argv[]) {
  int rc = 0;
  pthread_t thread[MAX_THREADS];
  pthread_attr_t thread_attr;

  pthread_attr_init(&thread_attr);
  pthread_attr_setstacksize(&thread_attr, PTHREAD_STACK_MIN);

  printf("Creating threads ...\n");
  for (i = 0; i < MAX_THREADS && rc == 0; i++) {
    rc = pthread_create(&(thread[i]), &thread_attr, (void *) &run, NULL);
    if (rc == 0) {
      pthread_detach(thread[i]);
      if ((i + 1) % 100 == 0)
    printf("%i threads so far ...\n", i + 1);
    }
    else
    {
      printf("Failed with return code %i creating thread %i (%s).\n",
         rc, i + 1, strerror(rc));

      // can we allocate memory?
      char *block = NULL;
      block = malloc(65545);
      if(block == NULL)
        printf("Malloc failed too :( \n");
      else
        printf("Malloc worked, hmmm\n");
    }
  }
sleep(60*60); // ctrl+c to exit; makes it easier to see mem use
  exit(0);
}

此外,您可以添加以下调用:pthread_attr_setguardsize(&thread_attr,0);就在pthread_attr_setstacksize()的调用之后,但这样会完全失去堆栈溢出检测,并且只会为您节省4K的地址空间和零实际内存。

0

可能的一个问题是主程序中的本地变量thread。我认为在您的64位机器上,pthread_t将会是8个字节(假设是64位构建)。这将会占用800,000字节的堆栈空间。您的512K堆栈限制可能会成为一个问题。512K / 8 = 65536,这个数字与您创建的线程数非常接近,您可以尝试动态分配该数组而不是将其放在堆栈上。


或者保持初始线程的堆栈大小不变,仅为后续线程更改大小(即针对创建的每个线程使用pthread_attr_setstack()设置堆栈大小)。 - Spudd86

0
你是否正在尝试寻找一个计算每个进程最大线程数的公式?

Linux间接实现了每个进程的最大线程数!!

number of threads = total virtual memory / (stack size*1024*1024)

因此,可以通过增加总虚拟内存或减小堆栈大小来增加每个进程的线程数。但是,过度减小堆栈大小可能会导致由于堆栈溢出而导致代码失败,而最大虚拟内存等于交换内存。

检查您的机器:

总虚拟内存:ulimit -v(默认为无限制,因此需要增加交换内存以增加此值)

总堆栈大小:ulimit -s(默认为8Mb)

增加这些值的命令:

ulimit -s newvalue

ulimit -v newvalue

*用你想要设定的限制值替换新值。

参考资料:

http://dustycodes.wordpress.com/2012/02/09/increasing-number-of-threads-per-process/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接