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)。
我试图找出问题所在的其中一项尝试是。
- 我使用了 pthread_join() 函数,这在我的情况下不是一个选项,只是为了确保每个线程都完成而不是 运行ning 在永久循环中。该程序 运行 宁了一个多小时,错误没有再次出现。当然每个线程都按预期完成了。
- 而且我 90% 确定每个主题都只在线程上创建,下一个主题只有在前一个主题完成后才会创建。 (我在线程创建前后跟踪变量的状态)。
- 我将此处“/proc/sys/kernel/threads-max”的最大线程设置为 2000(2000 绰绰有余,因为我没有太多主题)
上传函数(这将创建线程):
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" 得到处理。
我正在用 c 语言构建一个项目(使用 openwrt 作为 OS)将文件上传到 FTP 服务器。我正在使用 MQTT 处理传入数据。因此,对于我订阅的每个主题,我都会保存这些数据,然后将其上传到 FTP 服务器,为了让事情顺利进行,每次我需要上传一个文件时,我只使用一个线程来完成这项工作。 并确保程序不会 运行 太多线程,允许每个主题创建一个线程。我正在使用一个变量(比如 mutex 但它不是 pthread_mutex_t 因为我不需要阻塞线程,我想跳过这一步并上传下一个文件)。我虽然使用这种技术是安全的,但在 运行 运行程序 15 分钟后,我收到此错误 11,它表示当程序尝试创建线程时资源暂时不可用 (pthread_create)。 我试图找出问题所在的其中一项尝试是。
- 我使用了 pthread_join() 函数,这在我的情况下不是一个选项,只是为了确保每个线程都完成而不是 运行ning 在永久循环中。该程序 运行 宁了一个多小时,错误没有再次出现。当然每个线程都按预期完成了。
- 而且我 90% 确定每个主题都只在线程上创建,下一个主题只有在前一个主题完成后才会创建。 (我在线程创建前后跟踪变量的状态)。
- 我将此处“/proc/sys/kernel/threads-max”的最大线程设置为 2000(2000 绰绰有余,因为我没有太多主题)
上传函数(这将创建线程):
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" 得到处理。