Linux中进程的最大线程数是多少?

8

我写了一个简单的程序来计算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。
感谢您。

6
如果你需要超过 300 个线程,你应该重新考虑你的设计。 - Erik
2
无论限制是什么,您都不应该达到那个限制。您应该创建一个线程池(可能具有用户配置的大小)。 - khachik
@Erik和khachik:我现在只是在想如果需要怎么做!但是谢谢关于池的想法。 - Hosi
1MB的堆栈大小仍然太大了。错误代码12=内存不足。顺便说一下,strerror()将打印错误代码。尝试使用16k的堆栈大小。 - johnnycrash
可能是Linux中每个进程的最大线程数?的重复问题。 - user
5个回答

18

Linux有线程数量限制,可以通过在/proc/sys/kernel/threads-max中写入所需的限制来修改运行时的限制。默认值是从可用系统内存计算出来的。除了这个限制之外,还有另一个限制:/proc/sys/vm/max_map_count,它限制最大的映射段数,而最近的内核会为每个线程映射内存。如果你遇到了这个限制,增加该限制很安全。

然而,你遇到的限制是32位操作系统中虚拟内存不足。如果你的硬件支持,安装64位的Linux就没问题了。我可以轻松地启动8MB栈大小的30000个线程。该系统有一个Core 2 Duo处理器和8GB系统内存(同时我正在使用5GB进行其他操作),并且正在运行64位的Ubuntu和2.6.32内核。请注意,必须允许内存过度提交(/proc/sys/vm/overcommit_memory),否则系统将需要至少240GB的可提交内存(实际内存和交换空间的总和)。

如果你需要大量的线程,并且不能使用64位系统,你唯一的选择是尽量减少每个线程的内存使用,以节省虚拟内存。首先请求所需的最小栈大小。


Linux使用物理内存来计算最大线程数,因此每个系统的最大线程数都不同(https://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) > 64) threads = MAX_THREADS; else threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE, (u64) THREAD_SIZE * 8UL); if (threads > max_threads_suggested) threads = max_threads_suggested; max_threads = clamp_t(u64, threads, MIN_THREADS, MAX_THREADS); } - katta

6

您的系统限制可能不允许您映射所需的所有线程的堆栈。请查看/proc/sys/vm/max_map_count,并参考此答案。我不能100%确定这是您的问题,因为大多数人在更大的线程数时会遇到问题。


1
"/proc/sys/vm/max_map_count" 的值为 65536,我无法更改它! - Hosi
哦,那也不是答案。另一个尝试的方法是使用 pthread_attr_getstacksize 找到您的堆栈大小,并检查您的限制是否允许您 malloc 那么多的堆栈。 - Adrian Cox
getstacksize返回10MB,我将其减少到1MB,但仍然出现相同的错误! - Hosi

4
当我的线程数超过一定阈值时,我也遇到了同样的问题。这是因为在/etc/security/limits.conf中设置了用户级别限制(用户同时可以运行的进程数)为1024。
所以请检查您的/etc/security/limits.conf,并查找以下条目: username -/soft/hard -nproc 1024
将其更改为一些较大的值,例如100k(需要sudo特权/根权限),然后它应该适用于您。
要了解有关安全策略的更多信息,请参见http://linux.die.net/man/5/limits.conf

3

使用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来提高此限制。但这是否明智是另一回事...


2

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

编辑: 错误代码12 = 内存不足,因此我认为1MB堆栈对您来说仍然太大了。 编译为32位时,我可以获得100k堆栈以给我30k个线程。 超过30k个线程,我会收到错误代码11,这意味着不允许更多线程。 1MB堆栈在出现错误代码12之前给我约4k个线程。 10MB给我427个线程。 100MB给我42个线程。 1GB给我4个... 我们有64位操作系统和64 GB RAM。 您的操作系统是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位操作系统和64GB的内存。 - johnnycrash
感谢"johnnycrash"提供的代码,但我不熟悉"__sync_fetch_and_add",无法编译此测试应用程序。您能否提供更多相关信息?谢谢。 - Hosi
1
__synch来自gcc原子操作。http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html 我使用它来原子地增加/减少一个值,而不必使用互斥锁(互斥锁=慢)。我们在英特尔上使用gcc,当我编译32位时,我必须使用-m32 -march=i686。使用原子内置函数时需要-march。当我编译为64位时,我当然不需要使用-m32,但我也不需要-march=i686,这似乎很奇怪。我们在这里使用64位RedHat,我们有32或64 GB的RAM。您是否收到错误代码11或12? 11 =不允许更多线程,12 =内存不足。 - johnnycrash
__synch 只适用于英特尔……我认为。您可以在增量周围使用一个互斥锁/解锁,然后再在减量周围使用一个。虽然速度较慢,但效果相同。 - johnnycrash

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