pthread_create错误(11)资源暂时不可用如何解决?

How to solve pthread_create error (11) resource temporarily unavailable?

我正在用 c 语言构建一个项目(使用 openwrt 作为 OS)将文件上传到 FTP 服务器。我正在使用 MQTT 处理传入数据。因此,对于我订阅的每个主题,我都会保存这些数据,然后将其上传到 FTP 服务器,为了让事情顺利进行,每次我需要上传一个文件时,我只使用一个线程来完成这项工作。 并确保程序不会 运行 太多线程,允许每个主题创建一个线程。我正在使用一个变量(比如 mutex 但它不是 pthread_mutex_t 因为我不需要阻塞线程,我想跳过这一步并上传下一个文件)。我虽然使用这种技术是安全的,但在 运行 运行程序 15 分钟后,我收到此错误 11,它表示当程序尝试创建线程时资源暂时不可用 (pthread_create)。 我试图找出问题所在的其中一项尝试是。

上传函数(这将创建线程):

void uploadFile(<args...>, bool* locker_p){
    *locker_p = true;
    args->uploadLocker_p = uploadLocker_p;

    <do something here>

    pthread_t tid;
    int error = pthread_create(&tid, NULL, uploadFileThread, (void*)args);
        if(0 != error){
            printf("Couldn't run thread,(%d) => %s\n", error, strerror(error));
    }
        else{
            printf("Thread %d\n", tid);
    }
}

上传线程:

void *uploadFileThread(void *arg){ 
    typeArgs* args = (typeArgs*)arg;

   <do something like upload the file>

    *(args->uploadLocker_p) = false;
    free(args);

    return NULL;
    //pthread_exit(0);
}

已创建线程的默认堆栈大小消耗了过多的虚拟内存。

本质上,内核是在告诉你的进程它已经使用了太多的虚拟内存,它不敢再给它了,因为没有足够的 RAM 和交换来支持它,如果进程突然全部用上。

要修复,请创建一个属性,将每线程堆栈限制在合理范围内。如果您的线程不使用数组作为局部变量,或进行深度递归,那么 2*PTHREAD_STACK_MIN(来自 <limits.h>)是一个很好的大小。 该属性不会被 pthread_create() 调用消耗,它只是一个配置块,您可以为您创建的任意数量的线程使用相同的配置块,或者为每个线程创建一个新的配置块。

示例:

pthread_attr_t  attrs;
pthread_t       tid;
int             err;

pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2 * PTHREAD_STACK_MIN);
err = pthread_create(&tid, &attrs, uploadFileThread, (void *)args);
pthread_attr_destroy(&attrs);
if (err) {
    /* Failed, errno in err; use strerror(err) */
} else {
    /* Succeeded */
}

还要记住,如果你的uploadFileThread()分配内存,当线程退出时它不会自动释放。看起来 OP 已经知道这一点(因为他们有函数在准备退出时释放参数结构),但我认为指出它是个好主意。


就个人而言,我喜欢使用线程池。这个想法是预先创建上传工作人员,他们将等待新工作。这是一个例子:

pthread_mutex_t        workers_lock;
pthread_mutex_t        workers_wait;
volatile struct work  *workers_work;
volatile int           workers_idle;
volatile sig_atomic_t  workers_exit = 0;

其中struct work是一个被[=20=保护的单链表],workers_idle初始化为0,等待新工作时递增,workers_wait是一个条件当新工作到达 workers_lock 时发出信号的变量,workers_exit 是一个计数器,当非零时,告诉许多工人退出。

一个工人基本上就是这样

void worker_do(struct work *job)
{
   /* Whatever handling a struct job needs ... */
}

void *worker_function(void *payload __attribute__((unused)))
{
    /* Grab the lock. */
    pthread_mutex_lock(&workers_lock);

    /* Job loop. */
    while (!workers_exit) {
        if (workers_work) {
            /* Detach first work in chain. */
            struct work *job = workers_work;
            workers_work = job->next;
            job->next = NULL;

            /* Work is done without holding the mutex. */
            pthread_mutex_unlock(&workers_lock);
            worker_do(job);
            pthread_mutex_lock(&workers_lock);
            continue;
        }

        /* We're idle, holding the lock. Wait for new work. */
        ++workers_idle;
        pthread_cond_wait(&workers_wait, &workers_lock);
        --workers_idle;
    }

    /* This worker exits. */
    --workers_exit;

    pthread_mutex_unlock(&workers_lock);
    return NULL;
}

连接处理进程可以使用 idle_workers() 来检查空闲工作线程的数量,或者增加工作线程池,或者以太忙为由拒绝连接。 idle_workers() 类似于

static inline int  idle_workers(void)
{
    int  result;
    pthread_mutex_lock(&workers_lock);
    result = workers_idle;
    pthread_mutex_unlock(&workers_lock);
    return result;
}

请注意,每个工作人员只持有锁很短的时间,因此 idle_workers() 调用不会阻塞很长时间。 (pthread_cond_wait()开始等待信号时自动释放锁,重新获取锁后returns自动释放锁。)

accept()等待新连接时,设置socket为非阻塞,使用poll()等待新连接。如果超时过去了,检查工人的数量,并在必要时通过调用 reduce_workers(1) 或类似的方式减少他们:

void reduce_workers(int number)
{
    pthread_mutex_lock(&workers_lock);
    if (workers_exit < number) {
        workers_exit = number;
        pthread_cond_broadcast(&workers_wait);
    }
    pthread_mutex_unlock(&workers_lock);
}

为了避免必须为每个线程调用 pthread_join()——我们甚至不知道哪些线程已经退出了! – 对于reap/free与线程相关的内核和C库元数据,工作线程需要分离。创建工作线程tid成功后,调用pthread_detach(tid);.

即可

当一个新的连接到达并且它被确定为应该委托给工作线程的连接时,您可以,但不必检查空闲的数量线程,创建新的工作线程,拒绝上传,或者只是将工作添加到队列中,以便 "eventually" 得到处理。