【问题标题】:max thread per process in linuxLinux中每个进程的最大线程数
【发布时间】:2011-08-03 20:52:34
【问题描述】:

我写了一个简单的程序来计算一个进程在 linux (Centos 5) 中可以拥有的最大线程数。这是代码:

int main()
{
    pthread_t thrd[400];
    for(int i=0;i<400;i++)
    {
        int err=pthread_create(&thrd[i],NULL,thread,(void*)i);
        if(err!=0)
            cout << "thread creation failed: " << i <<" error code: " << err << endl;
    }
    return 0;
}

void * thread(void* i)
{
    sleep(100);//make the thread still alive
    return 0;
}

我发现线程的最大数量只有 300!?如果我需要更多怎么办? 不得不提的是 pthread_create 返回 12 作为错误代码。

感谢之前

【问题讨论】:

  • 如果你需要超过 300 个线程,你真的应该重新考虑你的设计
  • 你不应该达到这个限制。您应该创建一个线程池(可能具有用户配置的大小)。
  • @Erik & khachik:现在我只是想知道如果有必要该怎么做!但感谢池的想法。
  • 1mb 的堆栈大小仍然很大。错误代码 12 = 内存不足。 strerror() 将顺便打印错误代码。尝试 16k 堆栈大小。

标签: linux pthreads


【解决方案1】:

您的系统限制可能不允许您映射您需要的所有线程的堆栈。查看/proc/sys/vm/max_map_count,并查看this answer。我不能 100% 确定这是你的问题,因为大多数人在much larger thread counts 遇到问题。

【讨论】:

  • "/proc/sys/vm/max_map_count" 是 65536 我不能改!
  • 哦,好吧,这也不是答案。要尝试的另一件事是使用pthread_attr_getstacksize 查找您的堆栈大小,并检查您的限制是否允许您使用malloc 那么多堆栈。
  • getstacksize 返回 10MB,我将它减小到 1MB 但仍然是同样的错误!
【解决方案2】:

除非您缩小默认线程堆栈大小,否则您也会耗尽内存。在我们的 linux 版本上它是 10MB。

编辑: 错误代码 12 = 内存不足,所以我认为 1mb 堆栈对您来说仍然太大。编译为 32 位,我可以得到一个 100k 的堆栈来给我 30k 线程。超过 30k 线程我得到错误代码 11,这意味着不允许更多线程。在错误代码 12 之前,一个 1MB 的堆栈给了我大约 4k 个线程。10MB 给了我 427 个线程。 100MB 给了我 42 个线程。 1 GB 给了我 4... 我们有 64 位操作系统和 64 GB 内存。你的操作系统是 32 位的吗?当我为 64 位编译时,我可以使用任何我想要的堆栈大小并获得线程的限制。

我还注意到,如果我为 netbeans 打开分析工具(工具|分析)并从 ide 运行...我只能获得 400 个线程。诡异的。如果你用完所有线程,Netbeans 也会死掉。

这是一个您可以运行的测试应用:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>

// this prevents the compiler from reordering code over this COMPILER_BARRIER
// this doesnt do anything
#define COMPILER_BARRIER() __asm__ __volatile__ ("" ::: "memory")

sigset_t    _fSigSet;
volatile int    _cActive = 0;
pthread_t   thrd[1000000];

void * thread(void *i)
{
int nSig, cActive;

    cActive = __sync_fetch_and_add(&_cActive, 1);
    COMPILER_BARRIER();  // make sure the active count is incremented before sigwait

    // sigwait is a handy way to sleep a thread and wake it on command
    sigwait(&_fSigSet, &nSig); //make the thread still alive

    COMPILER_BARRIER();  // make sure the active count is decrimented after sigwait
    cActive = __sync_fetch_and_add(&_cActive, -1);
    //printf("%d(%d) ", i, cActive);
    return 0;
}

int main(int argc, char** argv)
{
pthread_attr_t attr;
int cThreadRequest, cThreads, i, err, cActive, cbStack;

    cbStack = (argc > 1) ? atoi(argv[1]) : 0x100000;
    cThreadRequest = (argc > 2) ? atoi(argv[2]) : 30000;

    sigemptyset(&_fSigSet);
    sigaddset(&_fSigSet, SIGUSR1);
    sigaddset(&_fSigSet, SIGSEGV);

    printf("Start\n");
    pthread_attr_init(&attr);
    if ((err = pthread_attr_setstacksize(&attr, cbStack)) != 0)
        printf("pthread_attr_setstacksize failed: err: %d %s\n", err, strerror(err));

    for (i = 0; i < cThreadRequest; i++)
    {
        if ((err = pthread_create(&thrd[i], &attr, thread, (void*)i)) != 0)
        {
            printf("pthread_create failed on thread %d, error code: %d %s\n", 
                     i, err, strerror(err));
            break;
        }
    }
    cThreads = i;

    printf("\n");

    // wait for threads to all be created, although we might not wait for 
    // all threads to make it through sigwait
    while (1)
    {
        cActive = _cActive;
        if (cActive == cThreads)
            break;
        printf("Waiting A %d/%d,", cActive, cThreads);
        sched_yield();
    }

    // wake em all up so they exit
    for (i = 0; i < cThreads; i++)
        pthread_kill(thrd[i], SIGUSR1);

    // wait for them all to exit, although we might be able to exit before 
    // the last thread returns
    while (1)
    {
        cActive = _cActive;
        if (!cActive)
            break;
        printf("Waiting B %d/%d,", cActive, cThreads);
        sched_yield();
    }

    printf("\nDone. Threads requested: %d.  Threads created: %d.  StackSize=%lfmb\n", 
     cThreadRequest, cThreads, (double)cbStack/0x100000);
    return 0;
}

【讨论】:

  • 1mb 太大了!试试16k。我可以用 1mb 获得 400 个线程,用 16k 可以获得 30k 线程我们有 64 位操作系统和 64 GB 内存。
  • 感谢“johnnycrash”为您提供代码,但我不熟悉“__sync_fetch_and_add”,无法编译此测试应用程序。你介意提供更多关于它的信息吗?谢谢
  • __synch 来自 gcc 原子操作。 gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html 我使用 em 以原子方式递增/递减一个值,而无需使用互斥体(互斥体 = 慢)。我们在 intel 上使用 gcc,当我编译 32 位时,我必须使用 -m32 -march=i686。使用 atomic 内置函数时需要 -march。当我编译到 64 位时,我当然不必使用 -m32,但我也不需要 -march=i686 这似乎很奇怪。我们在这里使用 64 位 RedHat,我们有 32 或 64 GB 内存。您收到错误代码 11 还是 12? 11=不允许更多线程,12=内存不足。
  • __synch 仅适用于英特尔...我认为。您可以使用围绕 inc 的互斥锁/解锁,然后在 dec 周围使用一个互斥锁/解锁。较慢但效果相同。
【解决方案3】:

linux 有一个线程限制,可以通过将所需限制写入/proc/sys/kernel/threads-max 来修改运行时。默认值是根据可用的系统内存计算得出的。除了这个限制之外,还有另一个限制:/proc/sys/vm/max_map_count,它限制了最大映射段,并且至少最近的内核将映射每个线程的内存。如果您达到该限制,则应该安全地增加该限制。

您遇到的限制是 32 位操作系统中缺少虚拟内存。如果您的硬件支持它,请安装 64 位 linux,您会没事的。我可以轻松启动 30000 个线程,堆栈大小为 8MB。该系统有一个 Core 2 Duo + 8 GB 系统内存(我同时将 5 GB 用于其他东西),它运行 64 位 Ubuntu,内核为 2.6.32。请注意,必须允许内存过度使用 (/proc/sys/vm/overcommit_memory),否则系统将需要至少 240 GB 的可提交内存(实际内存和交换空间的总和)。

如果您需要大量线程并且不能使用 64 位系统,您唯一的选择是最小化每个线程的内存使用量以节省虚拟内存。从请求尽可能少的堆栈开始。

【讨论】:

  • Linux 使用物理内存来计算最大线程数,因此每个系统都不同 (github.com/torvalds/linux/blob/master/kernel/fork.c) static void set_max_threads(unsigned int max_threads_suggested) { u64 threads; if (fls64(totalram_pages) + fls64(PAGE_SIZE) &gt; 64) threads = MAX_THREADS; else threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE, (u64) THREAD_SIZE * 8UL); if (threads &gt; max_threads_suggested) threads = max_threads_suggested; max_threads = clamp_t(u64, threads, MIN_THREADS, MAX_THREADS); }
【解决方案4】:

当我的线程数超过某个阈值时,我也遇到了同样的问题。 这是因为 /etc/security/limits.conf 中的用户级别限制(用户一次可以运行的进程数)设置为 1024。

所以检查您的 /etc/security/limits.conf 并查找条目:-

用户名 -/soft/hard -nproc 1024

将其更改为更大的值到 100k(需要 sudo 权限/root),它应该适合你。

要了解有关安全策略的更多信息,请参阅http://linux.die.net/man/5/limits.conf

【讨论】:

    【解决方案5】:

    使用 ulimit 检查每个线程的堆栈大小,在我的例子中是 Redhat Linux 2.6:

        ulimit -a
    ...
        stack size              (kbytes, -s) 10240
    

    您的每个线程都将获得为其堆栈分配的内存量 (10MB)。 32位程序,最大地址空间4GB,也就是最多只有4096MB / 10MB = 409个线程!!!减去程序代码,减去堆空间可能会导致您观察到的最大值。 300 个线程。

    您应该能够通过编译 64 位应用程序或设置 ulimit -s 8192 甚至 ulimit -s 4096 来提高这一点。但如果这是可取的,则另作讨论......

    【讨论】:

      猜你喜欢
      • 2013-06-09
      • 1970-01-01
      • 1970-01-01
      • 2020-11-02
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多