如何在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); }
代码说明
- 自定义
FileData结构:将所有相关的请求对象(open、read、close)和文件信息绑定在一起,方便在回调中传递数据。 - 请求对象独立性:每个文件对应独立的
FileData实例,确保异步操作不会互相干扰。 - 资源清理流程:每个请求完成后都调用
uv_fs_req_cleanup(),堆分配的资源在最终回调中统一释放。 - 错误处理:每个步骤都处理失败情况,避免资源泄漏。
内容的提问来源于stack exchange,提问作者samawaat
相关产品推荐
相关产品推荐

