[实战] Windows 文件读写函数 `ReadFile()` 和 `WriteFile()` 的阻塞与非阻塞操作详解(含完整C语言示例)

Windows 文件读写函数 ReadFile()WriteFile() 的阻塞与非阻塞操作详解(含完整C语言示例)

在 Windows 平台进行文件或设备(如串口、管道)编程时,ReadFile()WriteFile() 是最常用的两个 API 函数。它们既可以以阻塞方式运行,也可以通过设置标志实现非阻塞方式运行。本文将详细讲解这两种模式的区别,并提供完整的 C 语言示例代码。


一、概述:ReadFile()WriteFile() 简介

这两个函数定义在 windows.h 头文件中:

BOOL ReadFile(
  HANDLE       hFile,
  LPVOID       lpBuffer,
  DWORD        nNumberOfBytesToRead,
  LPDWORD      lpNumberOfBytesRead,
  LPOVERLAPPED lpOverlapped
);

BOOL WriteFile(
  HANDLE       hFile,
  LPCVOID      lpBuffer,
  DWORD        nNumberOfBytesToWrite,
  LPDWORD      lpNumberOfBytesWritten,
  LPOVERLAPPED lpOverlapped
);
  • hFile:文件或设备的句柄。
  • lpBuffer:数据缓冲区地址。
  • nNumberOfBytesTo(Read/Write):要读写的字节数。
  • lpNumberOfBytes(Read/Written):实际读写的数据长度。
  • lpOverlapped:重叠结构指针。为 NULL 表示同步(阻塞)操作;否则表示异步(非阻塞)操作。

二、阻塞操作 vs 非阻塞操作

特性阻塞操作(默认)非阻塞操作(异步)
是否等待完成是,调用线程会被挂起直到操作完成否,函数立即返回,操作由系统后台完成
是否需要 OVERLAPPED 结构
是否支持事件通知可结合事件对象进行通知
是否支持 I/O 完成端口
CPU 使用率较低(等待期间休眠)较高(需主动轮询或使用回调)
实现复杂度简单复杂

三、阻塞方式示例

✅ 示例1:阻塞方式读写文件

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

int main() {
    HANDLE hFile = CreateFile(
        TEXT("testfile.txt"),
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        printf("CreateFile failed (%d)\n", GetLastError());
        return 1;
    }

    const char* buffer = "Hello, World!";
    DWORD bytesWritten;

    // 写入文件(阻塞)
    if (!WriteFile(hFile, buffer, strlen(buffer), &bytesWritten, NULL)) {
        printf("WriteFile failed (%d)\n", GetLastError());
        CloseHandle(hFile);
        return 1;
    }
    printf("Wrote %d bytes\n", bytesWritten);

    // 移动文件指针到开头
    SetFilePointer(hFile, 0, NULL, FILE_BEGIN);

    char readBuffer[256];
    DWORD bytesRead;

    // 读取文件(阻塞)
    if (!ReadFile(hFile, readBuffer, sizeof(readBuffer), &bytesRead, NULL)) {
        printf("ReadFile failed (%d)\n", GetLastError());
        CloseHandle(hFile);
        return 1;
    }
    readBuffer[bytesRead] = '\0';  // 添加字符串结束符
    printf("Read: %s\n", readBuffer);

    CloseHandle(hFile);
    return 0;
}

📌 编译命令(MinGW / GCC):

gcc -o blocking_example blocking_example.c -mwindows

四、非阻塞方式示例(异步操作)

使用 OVERLAPPED 结构和事件对象可以实现非阻塞读写。下面是一个完整的异步读取示例:

✅ 示例2:非阻塞方式读取文件(异步 + 事件通知)

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

int main() {
    HANDLE hFile = CreateFile(
        TEXT("testfile.txt"),
        GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,  // 设置为异步操作
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        printf("CreateFile failed (%d)\n", GetLastError());
        return 1;
    }

    char buffer[256];
    DWORD bytesRead;
    OVERLAPPED overlapped = {0};
    overlapped.Offset = 0;
    overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  // 创建事件

    // 异步读取
    BOOL result = ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, &overlapped);
    if (!result && GetLastError() != ERROR_IO_PENDING) {
        printf("ReadFile error (%d)\n", GetLastError());
        CloseHandle(hFile);
        return 1;
    }

    printf("Waiting for asynchronous read to complete...\n");

    // 等待事件触发(可替换为 WaitForMultipleObjects 或其他机制)
    WaitForSingleObject(overlapped.hEvent, INFINITE);

    // 获取最终结果
    if (!GetOverlappedResult(hFile, &overlapped, &bytesRead, FALSE)) {
        printf("GetOverlappedResult failed (%d)\n", GetLastError());
    } else {
        buffer[bytesRead] = '\0';
        printf("Async read completed: %s\n", buffer);
    }

    CloseHandle(overlapped.hEvent);
    CloseHandle(hFile);
    return 0;
}

📌 编译命令:

gcc -o async_read async_read.c -mwindows

五、总结对比

功能阻塞方式非阻塞方式(异步)
是否立即返回
是否适合大量并发
是否支持事件通知
是否需要处理线程阻塞不需要需要配合事件、线程池或 I/O 完成端口
适用场景简单文件读写、调试高性能网络服务、串口通信、异步I/O

六、扩展建议

  • 对于高性能服务器程序,建议结合 I/O 完成端口(IOCP) 使用异步模型。
  • 如果需要同时处理多个异步请求,应使用 线程池WaitForMultipleObjects
  • 对于串口、管道等设备通信,通常推荐使用异步模式提升响应能力。

七、结语

Windows 提供了灵活的文件和设备读写接口,开发者可以根据需求选择阻塞模式非阻塞异步模式。理解两者的区别及使用方法,是编写高效 Windows 应用的关键一步。希望本篇博客能帮助你更好地掌握 ReadFile()WriteFile() 的使用技巧!


研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值