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

如何在不使用忙等的情况下检测AIO操作何时完成?

C语言AIO:替代忙等的两种方案(回调/主动等待)

Linux的POSIX AIO确实支持主动阻塞等待和异步回调通知两种方式,完全不用通过忙等浪费CPU资源。

一、主动阻塞等待(类似pthread_join)

可以使用aio_suspend()函数,它会阻塞当前线程,直到指定的一个或多个AIO操作完成,或者超时触发。用法示例:

#include <aio.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main() {
    struct aiocb cb;
    int fd = open("test_file.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    // 初始化AIO控制块
    char read_buf[1024];
    cb.aio_fildes = fd;
    cb.aio_buf = read_buf;
    cb.aio_nbytes = sizeof(read_buf);
    cb.aio_offset = 0;
    cb.aio_reqprio = 0;
    cb.aio_sigevent.sigev_notify = SIGEV_NONE; // 不使用信号通知

    // 启动异步读操作
    if (aio_read(&cb) == -1) {
        perror("aio_read failed");
        close(fd);
        return 1;
    }

    // 准备等待的AIO控制块列表(以NULL结尾)
    const struct aiocb* cblist[] = {&cb, NULL};
    // 阻塞等待操作完成,第三个参数为NULL表示无限等待
    if (aio_suspend(cblist, 1, NULL) == -1) {
        perror("aio_suspend failed");
        close(fd);
        return 1;
    }

    // 检查操作结果
    int error = aio_error(&cb);
    if (error != 0) {
        fprintf(stderr, "AIO operation failed: %s\n", strerror(error));
        close(fd);
        return 1;
    }
    ssize_t bytes_read = aio_return(&cb);
    printf("Successfully read %zd bytes\n", bytes_read);

    close(fd);
    return 0;
}

aio_suspend()的逻辑和pthread_join完全一致:挂起当前线程,直到目标AIO操作完成,期间不会占用CPU做无用循环。

二、异步回调通知

POSIX AIO本身没有直接提供回调函数接口,但可以结合信号机制实现类似效果:在aiocb结构体中指定一个信号,当AIO操作完成时,系统会向进程发送该信号,你可以在信号处理函数中执行后续逻辑。

示例代码:

#include <aio.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

// 信号处理函数,接收AIO完成通知
void aio_completion_handler(int signum, siginfo_t* info, void* context) {
    if (info->si_code == SI_ASYNCIO) {
        struct aiocb* cb = info->si_value.sival_ptr;
        // 检查操作结果
        int error = aio_error(cb);
        if (error != 0) {
            fprintf(stderr, "AIO failed: %s\n", strerror(error));
            return;
        }
        ssize_t bytes = aio_return(cb);
        printf("Callback triggered: Read %zd bytes\n", bytes);
        // 这里可以执行后续业务逻辑
    }
}

int main() {
    struct aiocb cb;
    int fd = open("test_file.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    // 初始化AIO控制块
    char read_buf[1024];
    cb.aio_fildes = fd;
    cb.aio_buf = read_buf;
    cb.aio_nbytes = sizeof(read_buf);
    cb.aio_offset = 0;
    cb.aio_reqprio = 0;
    // 设置信号通知:使用SIGUSR1,传递cb指针作为附加信息
    cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
    cb.aio_sigevent.sigev_signo = SIGUSR1;
    cb.aio_sigevent.sigev_value.sival_ptr = &cb;

    // 注册带参数的信号处理函数
    struct sigaction sa;
    sa.sa_sigaction = aio_completion_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;
    if (sigaction(SIGUSR1, &sa, NULL) == -1) {
        perror("sigaction failed");
        close(fd);
        return 1;
    }

    // 启动异步读操作
    if (aio_read(&cb) == -1) {
        perror("aio_read failed");
        close(fd);
        return 1;
    }

    // 主进程可以继续执行其他任务,无需忙等
    printf("Doing other work while AIO runs...\n");
    sleep(3); // 模拟耗时业务操作

    close(fd);
    return 0;
}

注意:信号处理函数中只能调用异步安全函数(比如write、_exit等),如果需要复杂逻辑,建议在信号处理函数中设置一个全局标志,然后在主循环中检查标志并处理。

另外,FreeBSD等系统的AIO支持SIGEV_THREAD模式,可以直接指定一个线程函数作为回调,系统会在操作完成时自动创建线程执行该函数,用法更简洁:

cb.aio_sigevent.sigev_notify = SIGEV_THREAD;
cb.aio_sigevent.sigev_notify_function = my_aio_callback;
cb.aio_sigevent.sigev_notify_attributes = NULL; // 使用默认线程属性

但目前Linux的POSIX AIO暂不支持该模式,只能通过信号模拟回调。

总结

  • 主动等待AIO完成:使用aio_suspend(),完全替代忙等的阻塞等待逻辑。
  • 异步回调通知:通过SIGEV_SIGNAL结合信号处理函数实现,Linux下需自行处理信号,BSD系统支持直接线程回调。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 11:42:45