繁体   English   中英

无锁“可关闭”MPSC 队列

[英]lock-free “closable” MPSC queue

多个生产者单一消费者场景,除了消费发生一次,之后队列“关闭”并且不允许更多工作。 我有一个 MPSC 队列,所以我尝试添加一个无锁算法来“关闭”队列。 我相信它是正确的,它通过了我的测试。 问题是当我尝试优化 memory 命令时它停止工作(我认为工作丢失,例如在队列关闭后排队)。 即使在具有“某种”强 memory model 的 x64 上,即使只有一个生产者。

我尝试微调 memory 订单被注释掉:

// thread-safe for multi producers single consumer use
// linked-list based, and so it's growable 
MPSC_queue work_queue;
std::atomic<bool> closed{ false };
std::atomic<int32_t> producers_num{ 0 };
bool produce(Work&& work)
{
    bool res = false;

    ++producers_num;
    // producers_num.fetch_add(1, std::memory_order_release);
    if (!closed)
    // if (!closed.load(std::memory_order_acquire))
    {
        work_queue.push(std::move(work));
        res = true;
    }
    --producers_num;
    // producers_num.fetch_sub(1, std::memory_order_release);

    return res;
}
void consume()
{
    closed = true;
    // closed.store(true, std::memory_order_release);

    while (producers_num != 0)
    // while (producers_num.load(std::memory_order_acquire) != 0)
        std::this_thread::yield();

    Work work;
    while (work_queue.pop(work))
        process(work);
}

我还尝试了std::memory_order_acq_relproducers_num进行读-修改-写操作,也不起作用。

一个奖励问题:

该算法与 MPSC 队列一起使用,该队列已经在内部进行了一些同步。 将它们结合起来以获得更好的性能会很好。 您知道“可关闭” MPSC 队列的任何此类算法吗?

我认为closed = true; 在第一次检查producers_num之前,确实需要 seq_cst 以确保它对其他线程可见。 否则,此排序是可能的:

  • 生产者: ++producers_num;
  • 消费者: producers_num == 0
  • 生产者: if (!closed)发现它仍然打开
  • 消费者: close.store(true, release)变得全局可见。
  • 消费者: work_queue.pop(work)发现队列为空
  • 生产者: work_queue.push(std::move(work)); 在消费者停止查找后将工作添加到队列中。

如果您在返回之前让消费者检查producers_num == 0 ,您仍然可以避免 seq_cst,例如

    while (producers_num != 0)
    // while (producers_num.load(std::memory_order_acquire) != 0)
        std::this_thread::yield();

    do {
        Work work;
        while (work_queue.pop(work))
            process(work);
    } while(producers_num.load(acquire) != 0);
    // safe if pop included a full barrier, I think

我不是 100% 确定我有这个权利,但我认为在完全障碍后检查producer_num就足够了。

但是producer端确实需要++producers_num; 至少是 acq_rel,否则它可以重新排序过去if (!closed) (在if(!closed)之前的获取栅栏也可能起作用)。


由于您只想使用一次队列,因此它不需要环绕并且可能会简单得多。 就像一个原子生产者位置计数器,作者递增以声明一个位置,如果他们得到 position > 大小,则队列已满。 不过,我还没有考虑完整的细节。

这可能会为上述问题提供更简洁的解决方案,也许是让消费者查看该写入索引以查看是否有任何生产者

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM