第98次调用pthread_create()失败

时间:2012-10-11 16:06:10

标签: c linux multithreading

我正在运行以下程序。它只是创建直接死亡的线程。

我发现在93到98(略有不同)成功调用之后,每次调用pthread_create()都会失败,错误11:资源暂时不可用。我认为我正在关闭线程所以它应该放弃它拥有的任何资源,但有些资源变得不可用。

程序的第一个参数允许我设置调用pthread_create()之间的间隔,但是使用不同的值进行测试,我已经知道间隔无关紧要(好吧,我会先得到错误):成功通话的次数将是相同的。

程序的第二个参数允许我在调用失败后设置一个休眠间隔,但是间隔的长度似乎没有任何区别。

我在这里打了哪个天花板?

编辑:在doSomething()中发现错误:将锁更改为解锁并且程序运行正常。问题仍然存在:未纠正错误导致哪些资源耗尽?

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <pthread.h>
#include <errno.h>

pthread_mutex_t doSomethingLock;

void milliSleep(unsigned int milliSeconds)
{
    struct timespec ts;

    ts.tv_sec = floorf(((float)milliSeconds / 1000));
    ts.tv_nsec = ((((float)milliSeconds / 1000) - ts.tv_sec)) * 1000000000;
    nanosleep(&ts, NULL);
}

void *doSomething(void *args)
{
    pthread_detach(pthread_self());
    pthread_mutex_lock(&doSomethingLock);
    pthread_exit(NULL);
}


int main(int argc, char **argv)
{
    pthread_t doSomethingThread;
    pthread_mutexattr_t attr;
    int threadsCreated = 0;


    if (argc != 3)
    {
        fprintf(stderr, "usage: demo <interval between pthread_create() in ms> <time to wait after fail in ms>\n");
        exit(1);
    }

    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
    pthread_mutex_init(&doSomethingLock, &attr);

    while (1)
    {
        pthread_mutex_lock(&doSomethingLock);
        if (pthread_create(&doSomethingThread, NULL, doSomething, NULL) != 0)
        {
            fprintf(stderr, "%d pthread_create(): error %d, %m\n", threadsCreated, errno);
            milliSleep(atoi(argv[2]));
        }
        else threadsCreated++;
        milliSleep(atoi(argv[1]));
    }
}

2 个答案:

答案 0 :(得分:5)

如果您使用的是32位发行版,则可能达到了地址空间限制。我检查的最后一次,glibc将为每个创建的线程分配大约13MB的堆栈空间(这只是映射的大小,而不是分配的内存)。使用98个线程,您将超过3G可用的千兆字节地址空间。

您可以通过在错误发生后冻结您的流程(例如sleep(1000000)或其他)并使用pmap查看其地址空间来对此进行测试。

如果这是问题所在,请尝试在传递给pthread_attr_setstack()的{​​{1}}上使用pthread_attr_t设置较小的堆栈大小。您必须明确地判断堆栈需求,但即使是复杂的代码也只能在几千字节的堆栈中成功运行。

答案 1 :(得分:2)

您的程序“创建简单消失的线程”。它没有按照你的想法做到。

首先,pthread_mutex_unlock()仅解锁已被同一线程锁定的pthread_mutex_t 。这就是互斥体的工作方式:它们只能通过锁定它们的同一个线程解锁。如果您想要信号量semaphoreuse a semaphore

的行为

您的示例代码会创建一个递归互斥锁,doSomething()函数会尝试锁定该互斥锁。因为它由原始线程保持,所以它会阻塞(等待pthread_mutex_lock()调用中的互斥锁变为空闲)。因为原始线程永远不会释放锁,所以只需在doSomethingLock互斥锁之上堆积新线程。

关于互斥锁的递归只意味着一个线程可以多次锁定它;它必须解锁它的次数才能实际释放互斥锁。

如果您将pthread_mutex_lock()中的doSomething()更改为pthread_mutex_unlock(),那么您正在尝试解锁该线程未持有的互斥锁。调用失败,然后线程立即死亡。


假设你修复了你的程序,接下来你会发现你不能创建超过一百个左右的线程(取决于你的系统和可用的RAM)。

Andy Ross很好地解释了这个原因:固定大小的堆栈(getrlimit(RLIMIT_STACK, (struct rlimit *)&info)告诉你,除非你通过线程属性设置它,否则占用你的可用地址空间。

为进程提供的原始堆栈会自动调整大小,但对于所有其他线程,堆栈大小是固定的。默认情况下,它非常大;在我的系统上,8388608字节(8兆字节)。

我个人创建具有非常小的堆栈的线程,通常是65536字节,除非你的函数使用本地数组或大型结构,否则这些线程已经足够了,或者进行疯狂的深度递归:

#ifndef THREAD_STACK_SIZE
#define THREAD_STACK_SIZE  65536
#endif

pthread_attr_t   attrs;
pthread_t        thread[N];
int              i, result;

/* Create a thread attribute for the desired stack size. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE);

/* Create any number of threads.
 * The attributes are only a guide to pthread_create(),
 * they are not "consumed" by the call. */
for (i = 0; i < N; i++) {
    result = pthread_create(&thread[i], &attrs, some_func, (void *)i);
    if (result) {
        /* strerror(result) describes the error */
        break;
    }
}

/* You should destroy the attributes when you know
 * you won't be creating any further threads anymore. */
pthread_attr_destroy(&attrs);

最小堆栈大小应该是PTHREAD_STACK_MIN,并且应该是sysconf(_SC_PAGESIZE)的倍数。目前PTHREAD_STACK_MIN == 16384,但我建议使用更大的2的幂。 (在任何二进制架构上,页面大小总是 2的幂。)

它只是最小的,并且pthread库可以自由地使用它认为合适的任何更大的值,但实际上堆栈大小似乎是你设置的,加上一个固定值,取决于体系结构,内核,和pthread库版本。使用编译时常量几乎适用于所有情况,但如果您的应用程序足够复杂以获得配置文件,那么让用户在配置中覆盖编译时常量可能是个好主意文件。