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

如何在libuv中发起多个文件系统请求?含请求对象处理及示例需求

libuv 多文件读取示例与请求对象说明

核心问题说明

你当前代码的关键错误是复用了同一个uv_fs_t请求对象。libuv的异步请求对象(比如uv_fs_t)在异步操作未完成前,会被libuv内部持有引用,复用会导致内存覆盖、崩溃等未定义行为。每个异步文件操作都需要独立的请求实例。

请求对象的定义与清理规则

  • 定义方式:
    • 栈分配:适合单次、短生命周期的操作,但要保证操作完成前,栈对象不会被销毁(比如不要在函数返回前发起异步操作,除非函数会等待操作完成)。
    • 堆分配:适合多文件、动态创建的操作,通过malloc/calloc分配,在回调函数中完成资源释放。
  • 清理要求:
    无论操作成功还是失败,在回调函数中必须调用uv_fs_req_cleanup(),释放请求对象内部的libuv管理资源。如果是堆分配的请求对象,清理后还要用free()释放内存。

完整示例代码

以下是打开多个文本文件、读取内容并打印的完整示例:

#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
#include <fcntl.h>
#include <unistd.h>

// 自定义数据结构,关联请求对象与文件信息
typedef struct {
    uv_fs_t open_req;
    uv_fs_t read_req;
    uv_fs_t close_req;
    const char* filename;
    char* buffer;
} FileData;

// 关闭文件后的回调
void on_close(uv_fs_t* req) {
    FileData* data = req->data;
    // 清理close请求
    uv_fs_req_cleanup(req);
    // 释放自定义数据结构和读取缓冲区
    free(data->buffer);
    free(data);
}

// 读取文件内容后的回调
void on_read(uv_fs_t* req) {
    FileData* data = req->data;
    if (req->result > 0) {
        // 打印读取到的内容,注意添加结束符
        data->buffer[req->result] = '\0';
        printf("文件 %s 内容:\n%s\n", data->filename, data->buffer);
        // 关闭文件
        uv_fs_close(uv_default_loop(), &data->close_req, (int)data->open_req.result, on_close);
        data->close_req.data = data;
    } else if (req->result < 0) {
        fprintf(stderr, "读取文件 %s 失败: %s\n", data->filename, uv_strerror(req->result));
        // 读取失败也要关闭文件
        if (data->open_req.result >= 0) {
            uv_fs_close(uv_default_loop(), &data->close_req, (int)data->open_req.result, on_close);
            data->close_req.data = data;
        } else {
            // 连打开都失败了,直接清理资源
            uv_fs_req_cleanup(req);
            free(data->buffer);
            free(data);
        }
    }
    // 清理read请求
    uv_fs_req_cleanup(req);
}

// 打开文件后的回调
void on_open(uv_fs_t* req) {
    FileData* data = req->data;
    if (req->result >= 0) {
        // 分配读取缓冲区,这里假设文件不超过1024字节,实际可根据文件大小调整
        data->buffer = malloc(1024);
        if (!data->buffer) {
            fprintf(stderr, "内存分配失败\n");
            uv_fs_close(uv_default_loop(), &data->close_req, (int)req->result, on_close);
            data->close_req.data = data;
            uv_fs_req_cleanup(req);
            return;
        }
        // 发起读取请求
        uv_fs_read(uv_default_loop(), &data->read_req, (int)req->result,
                   (uv_buf_t){.base = data->buffer, .len = 1023}, 0, on_read);
        data->read_req.data = data;
    } else {
        fprintf(stderr, "打开文件 %s 失败: %s\n", data->filename, uv_strerror(req->result));
        // 清理open请求,释放资源
        uv_fs_req_cleanup(req);
        free(data);
    }
    // 清理open请求
    uv_fs_req_cleanup(req);
}

int main() {
    uv_loop_t* loop = uv_default_loop();
    // 要读取的文件列表
    const char* filenames[] = {"file1.txt", "file2.txt", NULL};

    for (int i = 0; filenames[i]; i++) {
        // 为每个文件分配独立的FileData结构
        FileData* data = malloc(sizeof(FileData));
        if (!data) {
            fprintf(stderr, "内存分配失败\n");
            continue;
        }
        data->filename = filenames[i];
        // 发起打开文件请求
        uv_fs_open(loop, &data->open_req, filenames[i], O_RDONLY, 0, on_open);
        data->open_req.data = data;
    }

    // 运行事件循环
    return uv_run(loop, UV_RUN_DEFAULT);
}

代码说明

  1. 自定义FileData结构:将所有相关的请求对象(open、read、close)和文件信息绑定在一起,方便在回调中传递数据。
  2. 请求对象独立性:每个文件对应独立的FileData实例,确保异步操作不会互相干扰。
  3. 资源清理流程:每个请求完成后都调用uv_fs_req_cleanup(),堆分配的资源在最终回调中统一释放。
  4. 错误处理:每个步骤都处理失败情况,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 17:32:43