我正在用c语言构建一个项目(使用openwrt作为操作系统)将文件上传到FTP服务器。我用MQTT来处理输入的数据。因此,对于我订阅的每个主题,我保存这些数据,然后将其上传到FTP服务器,为了保持工作顺利进行,每次我需要上传一个文件时,我只是使用一个线程来完成这个工作。为了确保程序不会运行太多线程,每个主题都可以创建一个线程。我使用一个变量(比如互斥变量,但它不是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);
}
发布于 2020-06-16 14:51:49
创建的线程的默认堆栈大小占用了太多的虚拟内存。
本质上,内核告诉您的进程,它已经使用了这么多虚拟内存,所以它不敢再给它了,因为如果进程突然使用它,就没有足够的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
是受workers_lock
保护的单链列表时,workers_idle
被初始化为零并在等待新工作时增加,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()
在开始等待信号时原子地释放锁,并且只有在重新获得锁之后才返回。)
在accept()
中等待新连接时,设置套接字非阻塞,并使用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()
--我们甚至不知道这里有哪些线程退出了!--为了获取/释放与线程相关的内核和C库元数据,需要分离工作线程。成功创建工作线程tid
后,只需调用pthread_detach(tid);
即可。
当一个新连接到达并被确定为应该委托给工作线程时,您可以(但不必)检查空闲线程的数量、创建新的辅助线程、拒绝上载或只是将工作附加到队列中,以便“最终”处理它。
https://stackoverflow.com/questions/62409492
复制相似问题