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

多线程进程中使用io_uring的无锁线程协作最佳方案咨询

多线程下无锁使用io_uring的最佳实践

你的问题根源在于:当多个线程同时对同一个io_uring实例调用io_uring_wait_cqe时,内核的CQE等待机制是广播唤醒的,可能导致同一个CQE被多个线程获取并重复处理。以下是几种无需锁/互斥量的解决方案,按实用性排序:

方案1:单线程独占CQE收割,多线程处理业务逻辑

这是最通用、高效的无锁模式,核心是把CQE的获取和业务逻辑解耦:

  • 步骤:
    1. 初始化一个全局io_uring实例(按需配置IORING_SETUP_SQPOLL等性能优化标志)。
    2. 启动一个专门的「CQE收割线程」,循环执行:
      struct io_uring_cqe *cqe;
      // 批量获取CQE提升效率,也可以用io_uring_wait_cqe
      int ret = io_uring_peek_batch_cqe(&ring, &cqe, 32);
      if (ret > 0) {
          for (int i = 0; i < ret; i++) {
              // 将CQE对应的任务(如读写完成后的业务逻辑)放入无锁MPSC队列
              enqueue_task(cqe[i].user_data, cqe[i].res);
              // 标记CQE已处理
              io_uring_cqe_seen(&ring, &cqe[i]);
          }
      }
      
    3. 启动多个工作线程,从无锁MPSC队列中取出任务并处理。如果处理过程中需要提交新的IO请求(如继续读取客户端数据),直接调用io_uring_submit提交SQE即可(SQE提交是线程安全的,只要不并发修改同一个SQE结构体)。
  • 优势:完全避免CQE竞争,收割线程独占CQ队列访问,工作线程专注CPU密集型业务,IO和CPU资源利用更均衡。

方案2:每个线程使用独立的io_uring实例

如果你的业务场景可以按连接/任务分片(比如按客户端IP哈希分配线程),可以让每个工作线程拥有自己的io_uring实例:

  • 步骤:
    1. 每个线程初始化独立的io_uring实例,可开启IORING_SETUP_SQPOLL减少系统调用开销。
    2. 线程仅负责提交自己分片内的IO请求,并自行调用io_uring_wait_cqe处理对应的CQE。
    3. 线程间完全隔离,无需任何同步机制。
  • 优势:彻底消除共享资源,每个线程自给自足,适合IO密集型且任务独立性强的场景(如高并发代理服务器)。

方案3:原子标记过滤重复CQE(不推荐,仅作为兜底)

如果必须让多个线程处理同一个io_uring的CQE,可以通过原子变量标记CQE是否已被处理:

  • 实现思路:
    在提交SQE时,将user_data指向一个包含原子布尔值的结构体:
    struct task_data {
        _Atomic bool processed;
        // 其他业务数据
        int fd;
        char buf[1024];
    };
    
    // 提交SQE时初始化
    struct task_data *data = malloc(sizeof(*data));
    atomic_store(&data->processed, false);
    struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
    io_uring_prep_read(sqe, data->fd, data->buf, sizeof(data->buf), 0);
    sqe->user_data = (unsigned long)data;
    
    线程获取CQE后先尝试原子CAS标记为已处理:
    struct io_uring_cqe *cqe;
    io_uring_wait_cqe(&ring, &cqe);
    struct task_data *data = (struct task_data *)cqe->user_data;
    if (atomic_compare_exchange_strong(&data->processed, &false, true)) {
        // 处理业务逻辑
        handle_task(data, cqe->res);
        free(data);
    }
    io_uring_cqe_seen(&ring, cqe);
    
  • 劣势:会出现线程被唤醒后发现CQE已被处理的无效唤醒,浪费CPU资源,仅适合特殊场景下临时兜底。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 02:55:16