You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言Windows环境下生产者多线程消费者队列实现文件备份的相关问题

Windows纯C多线程备份任务队列实现方案

采用生产者-消费者模型实现,扫描线程作为生产者负责将待备份文件名塞到队列,4个工作线程作为消费者从队列取任务执行备份,全程不会阻塞扫描逻辑,也不会出现重复任务。


依赖组件说明

全部使用Windows原生API实现,无需第三方依赖:

  • 用CRITICAL_SECTION临界区做队列锁,用户态同步速度极快,不会拖慢扫描线程
  • 用Semaphore信号量做任务计数,工作线程无任务时阻塞在信号量上,不占用CPU资源
  • 文件名单独堆内存分配,避免扫描线程栈变量释放后野指针问题

核心结构定义

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 队列节点:存储待备份文件名
typedef struct TaskNode {
    char* filename;
    struct TaskNode* next;
} TaskNode;

// 线程安全的任务队列
typedef struct TaskQueue {
    TaskNode* head;
    TaskNode* tail;
    CRITICAL_SECTION lock; // 队列操作锁
    HANDLE semaphore;      // 任务计数信号量
    int shutdown;          // 退出标志位
} TaskQueue;

// 全局队列实例,也可以封装后作为参数传递
TaskQueue g_backup_queue;

队列基础接口实现

队列初始化

void queue_init(TaskQueue* q) {
    q->head = q->tail = NULL;
    InitializeCriticalSection(&q->lock);
    // 信号量初始计数0,最大计数设为10万满足普通文件扫描量级,可按需调整
    q->semaphore = CreateSemaphore(NULL, 0, 100000, NULL);
    q->shutdown = 0;
}

任务入队(扫描线程调用)

操作仅持有极短时间锁,完全不会阻塞扫描流程:

void queue_push(TaskQueue* q, const char* filename) {
    // 先复制文件名,避免扫描线程栈内存释放后出现野指针
    char* fname_buf = malloc(strlen(filename) + 1);
    strcpy(fname_buf, filename);

    TaskNode* node = malloc(sizeof(TaskNode));
    node->filename = fname_buf;
    node->next = NULL;

    // 临界区仅操作链表指针,耗时<1us,几乎无阻塞
    EnterCriticalSection(&q->lock);
    if (q->tail == NULL) {
        q->head = q->tail = node;
    } else {
        q->tail->next = node;
        q->tail = node;
    }
    LeaveCriticalSection(&q->lock);

    // 信号量计数+1,唤醒一个等待的工作线程
    ReleaseSemaphore(q->semaphore, 1, NULL);
}

任务出队(工作线程调用)

无任务时自动阻塞,不消耗CPU:

char* queue_pop(TaskQueue* q) {
    // 等待信号量,有任务或者队列关闭时才会返回
    WaitForSingleObject(q->semaphore, INFINITE);

    EnterCriticalSection(&q->lock);
    if (q->shutdown) {
        LeaveCriticalSection(&q->lock);
        return NULL;
    }

    TaskNode* node = q->head;
    q->head = node->next;
    if (q->head == NULL) {
        q->tail = NULL;
    }
    LeaveCriticalSection(&q->lock);

    char* fname = node->filename;
    free(node);
    return fname;
}

工作线程实现

// 你原有backup函数逻辑保留即可
int backup(char* filename) {
    // 压缩、移动到备份盘逻辑
    printf("备份文件: %s\n", filename);
    return 1;
}

DWORD WINAPI backup_worker(LPVOID param) {
    while (1) {
        char* fname = queue_pop(&g_backup_queue);
        if (fname == NULL) {
            // 收到退出信号,线程结束
            break;
        }
        backup(fname);
        free(fname); // 用完释放文件名内存
    }
    return 0;
}

扫描逻辑修改

原recursive_file_scan无需等待,扫到文件直接塞队列即可:

void recursive_file_scan(char* base_path) {
    // 你原有遍历逻辑保持不变
    /*
    ...遍历逻辑...
    if(is_folder(file_path)) 
        recursive_file_scan(file_path);
    */
    
    // 扫到文件直接入队,不需要等待备份完成,马上继续遍历
    char* filename = file_path;
    queue_push(&g_backup_queue, filename);
}

主流程调用示例

int main() {
    // 1. 初始化队列
    queue_init(&g_backup_queue);

    // 2. 启动4个工作线程
    HANDLE workers[4];
    for (int i = 0; i < 4; i++) {
        workers[i] = CreateThread(NULL, 0, backup_worker, NULL, 0, NULL);
    }

    // 3. 启动递归扫描,不会被备份逻辑阻塞
    recursive_file_scan("C:\\需要备份的目录");

    // 4. 扫描完成,通知所有工作线程退出
    EnterCriticalSection(&g_backup_queue.lock);
    g_backup_queue.shutdown = 1;
    LeaveCriticalSection(&g_backup_queue.lock);
    // 发4次信号量,唤醒所有阻塞的工作线程
    ReleaseSemaphore(g_backup_queue.semaphore, 4, NULL);

    // 5. 等待所有备份完成,释放资源
    WaitForMultipleObjects(4, workers, TRUE, INFINITE);
    for (int i = 0; i < 4; i++) {
        CloseHandle(workers[i]);
    }
    CloseHandle(g_backup_queue.semaphore);
    DeleteCriticalSection(&g_backup_queue.lock);

    return 0;
}

关键特性说明

  • 无扫描卡顿:入队操作临界区持有时间极短,扫描线程几乎不会被阻塞,遍历速度和单线程扫盘一致
  • 无重复备份:每个文件只会被扫描线程入队一次,队列出队操作加锁保证同一个任务只会被一个工作线程取到,不会重复执行
  • 资源安全:文件名单独堆分配,不会出现栈变量野指针问题,所有内存都有对应释放逻辑
  • 无空转消耗:工作线程无任务时阻塞在信号量上,不会占用CPU资源

内容的提问来源于stack exchange,提问作者mov eax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 23:24:03