C 如何解决pthread_创建错误(11)资源暂时不可用?
我正在用c语言构建一个项目(使用openwrt作为操作系统)将文件上传到FTP服务器。我对传入的数据使用MQTT。因此,对于我订阅的每个主题,我保存这些数据,然后将其上传到FTP服务器,为了保持事情的顺利进行,每次我需要上传一个文件时,我只使用一个线程来完成这项工作。 为了确保程序不会运行太多线程,允许每个主题创建一个线程。我使用了一个变量(比如mutex,但它不是pthread\u mutex\t,因为我不需要阻止线程,我想跳过这一步并上传下一个文件)。我认为使用这种技术我是安全的,但是在运行程序15分钟后,我得到了这个错误11,当程序尝试创建线程时,资源暂时不可用(pthread_create)。 我的一次尝试是找出问题所在C 如何解决pthread_创建错误(11)资源暂时不可用?,c,linux,multithreading,pthreads,openwrt,C,Linux,Multithreading,Pthreads,Openwrt,我正在用c语言构建一个项目(使用openwrt作为操作系统)将文件上传到FTP服务器。我对传入的数据使用MQTT。因此,对于我订阅的每个主题,我保存这些数据,然后将其上传到FTP服务器,为了保持事情的顺利进行,每次我需要上传一个文件时,我只使用一个线程来完成这项工作。 为了确保程序不会运行太多线程,允许每个主题创建一个线程。我使用了一个变量(比如mutex,但它不是pthread\u mutex\t,因为我不需要阻止线程,我想跳过这一步并上传下一个文件)。我认为使用这种技术我是安全的,但是在运行
- 我使用了pthread_join()函数,这在我的条件下不是一个选项,只是为了确保每个线程都已完成,并且没有在永久循环中运行。程序运行了一个多小时,错误没有再次出现。当然,每一根线都是按预期完成的
- 我90%确信每个主题只在线程上创建,下一个主题只有在前一个主题完成后才会创建。(我正在跟踪线程创建前后变量的状态)
- 我从这里将max thread“/proc/sys/kernel/threads max”设置为2000(2000已经足够了,因为我没有太多的主题)
void上传文件(,bool*locker\p){
*locker_p=真;
args->uploadLocker\u p=uploadLocker\u p;
pthread_t tid;
int error=pthread_create(&tid,NULL,uploadFileThread,(void*)参数);
如果(0!=错误){
printf(“无法运行线程,(%d)=>%s\n”,错误,strerror(错误));
}
否则{
printf(“线程%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);
}
void*上传文件线程(void*arg){
typeArgs*args=(typeArgs*)arg;
*(args->uploadLocker\u p)=错误;
免费(args);
返回NULL;
//pthread_退出(0);
}
创建的线程的默认堆栈大小占用了太多的虚拟内存
本质上,内核是在告诉您的进程,它已经有太多的虚拟内存在使用,它不敢再给它了,因为如果进程突然全部使用,没有足够的RAM和交换来备份它
要修复此问题,请创建一个属性,将每线程堆栈限制为合理的值。如果您的线程不使用数组作为局部变量,或者不执行深度递归,那么2*PTHREAD\u STACK\u MIN
(来自
)是一个不错的大小。
该属性不被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
是受workers\u lock
保护的单链列表,workers\u idle
初始化为零,并在等待新工作时递增,workers\u wait
是在workers\u lock
下到达新工作时发出信号的条件变量,而workers\u exit
是一个计数器,当非零时,它会告诉许多worker退出
一个工人基本上是一个好东西
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\u workers()
检查空闲工作线程的数量,或者增加工作线程池,或者因为太忙而拒绝连接。idle\u workers()
类似于
static inline int idle_workers(void)
{
int result;
pthread_mutex_lock(&workers_lock);
result = workers_idle;
pthread_mutex_unlock(&workers_lock);
return result;
}
请注意,每个worker只在很短的时间内持有锁,因此idle\u workers()
调用不会阻塞很长时间。(pthread_cond_wait()
在开始等待信号时自动释放锁,并且仅在重新获取锁后返回。)
在accept()
中等待新连接时,将套接字设置为非阻塞,并使用poll()
等待新连接。如果超时时间已过,请检查工作人员的数量,如有必要,可通过调用reduce\u 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);
}
void reduce\u workers(整数)
{
pthread_mutex_lock(&workers_lock);
如果(工人退出<数量){
工人退出=数量;
pthread_cond_广播(&workers_wait);
}
pthread_mutex_unlock(&workers_lock);
}
为了避免为每个线程调用pthread\u join()
,我们甚至不知道哪些线程已经退出了这里要获取/释放与线程相关的内核和C库元数据,需要分离工作线程。成功创建工作线程tid
后,只需调用pthread\u detach(tid)代码>
当一个新连接到达并且被确定为应该委派给工作线程的连接时,您可以(但不必)检查空闲线程的数量、创建新的工作线程、拒绝上载或只是将工作附加到队列中,以便它“最终”无法处理。所创建线程的默认堆栈大小占用了太多虚拟内存
本质上,内核是在告诉您的进程,它已经有太多的虚拟内存在使用,它不敢再给它了,因为没有足够的RAM和交换来备份它
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);
}