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

无锁多生产者单消费者场景下避免丢失唤醒的方案探讨

解决无锁MPSC队列的条件变量唤醒丢失问题

针对你遇到的唤醒丢失场景,这里提供两个无需阻塞数据线程的解决方案,完全符合低延迟需求:

方案一:原子状态标记+条件变量谓词检查

核心思路是用原子变量标记队列是否存在未处理任务,让工作线程进入休眠前做双重检查,避免错过窗口内到来的任务。

代码实现:

#include <atomic>
#include <mutex>
#include <condition_variable>

std::atomic<bool> has_pending_tasks = false;
std::mutex mutex_;
std::condition_variable cv_;
MpscJobQueue queue_;

// 工作线程逻辑
while (true) {
    // 处理队列中所有现有任务
    while (!queue_.empty()) {
        process(queue_.pop());
    }
    // 标记当前无待处理任务(内存序确保队列操作可见)
    has_pending_tasks.store(false, std::memory_order_release);
    
    std::unique_lock<std::mutex> lock(mutex_);
    // 条件变量的谓词会再次检查状态:
    // 如果数据线程在工作线程设置标记后、进入休眠前推送了任务,直接退出等待
    cv_.wait(lock, [&]() {
        return has_pending_tasks.load(std::memory_order_acquire);
    });
}

// 数据线程逻辑
queue_.push(myData);
// 标记有新任务,内存序确保队列写入对工作线程可见
has_pending_tasks.store(true, std::memory_order_release);
cv_.notify_one();

方案优势:

  • 数据线程全程不触碰互斥锁,完全不会被挂起,满足低延迟要求;
  • 彻底避免唤醒丢失:即使notify发生在工作线程进入wait之前,谓词检查也能发现新任务,工作线程会立刻回到外层循环处理队列;
  • 内存序的使用确保了队列操作和状态标记的可见性,无内存一致性问题。

方案二:自旋锁+std::condition_variable_any

利用自旋锁用户态忙等的特性,既保证工作线程进入休眠前的窗口不会错过任务,又不会让数据线程陷入内核态挂起。

代码实现:

#include <atomic>
#include <condition_variable>

// 轻量自旋锁实现
struct SpinLock {
    std::atomic<bool> locked = false;
    
    void lock() {
        // 忙等时加入pause指令减少CPU空转(x86平台)
        while (locked.exchange(true, std::memory_order_acquire)) {
            __builtin_ia32_pause();
        }
    }
    
    void unlock() {
        locked.store(false, std::memory_order_release);
    }
};

SpinLock spin_lock_;
std::condition_variable_any cv_;
MpscJobQueue queue_;

// 工作线程逻辑
while (true) {
    while (!queue_.empty()) {
        process(queue_.pop());
    }
    
    std::unique_lock<SpinLock> lock(spin_lock_);
    // 谓词直接检查队列是否为空
    cv_.wait(lock, [&]() { return !queue_.empty(); });
}

// 数据线程逻辑
queue_.push(myData);
{
    // 自旋锁的忙等不会导致线程挂起,延迟可忽略
    std::lock_guard<SpinLock> lock(spin_lock_);
}
cv_.notify_one();

方案优势:

  • 数据线程仅在极端情况(工作线程刚好处于退出内层循环到进入wait的窗口)下会短暂自旋,不会被内核挂起,符合低延迟要求;
  • 工作线程休眠时会释放自旋锁,不影响数据线程后续操作;
  • 逻辑更直观,直接通过锁同步工作线程进入休眠的时机和数据线程的notify时机。

方案对比

  • 优先选择原子状态标记方案,它的开销更低,数据线程完全无锁,适配你的场景;
  • 自旋锁方案适合需要更直观同步逻辑的场景,虽然数据线程有极短暂的忙等,但对你的低延迟需求来说完全可接受。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 13:25:56