高效文件拷贝:使用线程池优化大目录复制
这段代码使用线程池来完成大目录的复制,它通过将每个文件拷贝操作作为单独的任务添加到线程池中来实现并发。然而,在拷贝大量文件时,可能会遇到堆栈溢出问题,这是因为每次拷贝一个文件都会创建一个新的任务节点,并将其添加到任务队列中,导致任务队列过长,最终导致堆栈溢出。
以下是一些解决堆栈溢出问题的方案:
-
使用临时队列: 在创建任务节点时,不立即将其添加到任务队列中,而是先将任务节点放入一个临时的队列中。在合适的时候,比如当临时队列中的任务数量达到一定程度时,再将临时队列中的任务节点一次性地添加到任务队列中。这样可以减少任务节点的创建和添加次数,从而降低堆栈溢出的风险。
-
动态调整线程池大小: 可以根据任务队列中的任务数量来动态调整线程池的大小。当任务队列中的任务数量较多时,可以增加线程数量来提高并发性;当任务队列中的任务数量较少时,可以减少线程数量来节省资源。
-
优化代码: 可以优化代码,例如减少任务节点的大小,或者使用更轻量级的任务队列实现,以减少堆栈的消耗。
以下是一些示例代码,展示了如何使用临时队列来优化代码:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
// 任务节点结构体
struct task {
void *(*do_task)(void *arg);
void *arg;
struct task *next;
};
// 线程池结构体
struct thread_pool {
pthread_mutex_t lock;
pthread_cond_t cond;
struct task *task_list;
int waiting_tasks;
int max_waiting_tasks;
int shutdown;
pthread_t *tids;
int active_threads;
};
// 临时队列结构体
struct temp_queue {
struct task *head;
struct task *tail;
};
// 初始化临时队列
void init_temp_queue(struct temp_queue *queue) {
queue->head = NULL;
queue->tail = NULL;
}
// 添加任务到临时队列
void add_task_to_temp_queue(struct temp_queue *queue, struct task *task) {
if (queue->head == NULL) {
queue->head = task;
queue->tail = task;
} else {
queue->tail->next = task;
queue->tail = task;
}
}
// 从临时队列中获取任务
struct task *get_task_from_temp_queue(struct temp_queue *queue) {
struct task *task = queue->head;
if (task != NULL) {
queue->head = task->next;
if (queue->head == NULL) {
queue->tail = NULL;
}
}
return task;
}
// 线程执行函数
void *routine(void *arg) {
struct thread_pool *pool = (struct thread_pool *)arg;
struct task *p;
struct temp_queue temp_queue;
init_temp_queue(&temp_queue);
while (1) {
pthread_mutex_lock(&pool->lock);
while (pool->waiting_tasks == 0 && !pool->shutdown) {
pthread_cond_wait(&pool->cond, &pool->lock);
}
if (pool->waiting_tasks == 0 && pool->shutdown == true) {
pthread_mutex_unlock(&pool->lock);
pthread_exit(NULL);
}
// 从临时队列中获取任务
p = get_task_from_temp_queue(&temp_queue);
if (p == NULL) {
// 如果临时队列为空,从任务队列中获取任务
p = pool->task_list->next;
pool->task_list->next = p->next;
pool->waiting_tasks--;
}
pthread_mutex_unlock(&pool->lock);
// 执行任务
(p->do_task)(p->arg);
free(p);
}
pthread_exit(NULL);
}
// 初始化线程池
bool init_pool(struct thread_pool *pool, unsigned int threads_number) {
// ...
}
// 添加任务到临时队列
bool add_task_to_temp_queue(struct thread_pool *pool, void *(*do_task)(void *arg), void *arg) {
// 创建新的任务节点
struct task *new_task = malloc(sizeof(struct task));
if (new_task == NULL) {
perror("新任务申请空间失败\n");
return false;
}
new_task->do_task = do_task;
new_task->arg = arg;
new_task->next = NULL;
// 将任务添加到临时队列
add_task_to_temp_queue(&temp_queue, new_task);
// 当临时队列中的任务数量达到一定程度时,将临时队列中的任务添加到任务队列
if (temp_queue.tail->next != NULL) {
pthread_mutex_lock(&pool->lock);
// 将临时队列中的任务添加到任务队列
// ...
pthread_mutex_unlock(&pool->lock);
pthread_cond_signal(&pool->cond);
}
return true;
}
// 复制整个文件夹
int copy_wholedir(struct file_path *init_path) {
// ...
if(ep2->d_type==8) {
// 将文件拷贝任务添加到临时队列
add_task_to_temp_queue(pool, copy_single, (void *)tmp_path);
}
// ...
}
通过使用临时队列,可以有效地减少任务节点的创建和添加次数,降低堆栈溢出的风险。此外,还可以根据实际情况调整临时队列的大小和任务添加到任务队列的时机,以获得最佳的性能。
除了上述方案之外,还可以考虑以下措施:
- 使用更轻量级的任务队列实现,例如使用链表或环形缓冲区来代替数组。
- 减少任务节点的大小,例如将任务节点中的参数设置为指针,而不是直接存储数据。
- 使用更强大的编译器和运行时环境,以获得更好的内存管理和堆栈保护。
通过综合运用这些方法,可以有效地解决线程池中出现的堆栈溢出问题,并提高大目录复制的效率和稳定性。
原文地址: http://www.cveoy.top/t/topic/olcM 著作权归作者所有。请勿转载和采集!