0

我有一个问题,如果在 x86 Linux 上运行多个生产者和单个消费者的 lmax 破坏者(如环形缓冲区)中的消费者缓慢,该怎么办。使用类似环形缓冲区的 lmax 模式,您会不断地覆盖数据,但如果消费者速度很慢怎么办。因此,您如何处理在 10 大小的环形缓冲区 0-9 环形槽中,您的消费者位于槽 5 并且现在您的编写者已准备好开始写入槽 15,这也是缓冲区中的槽 5(即:槽5 = 15 % 10)? 处理此问题的典型方法是什么,使得编写者仍然按传入的顺序生成数据,而客户端将以相同的顺序接收数据?这真的是我的问题。下面是关于我的设计的一些细节,它工作正常,只是我目前没有一个好的方法来处理这个问题。

设计细节

我有一个环形缓冲区,设计目前有多个生产者线程和一个消费者线程。这部分设计是存在的,目前无法更改。我正在尝试使用无锁环形缓冲区删除现有的排队系统。我所拥有的如下。

代码在 x86 Linux 上运行,编写器运行多个线程,读取器运行单个线程。读取器和写入器从一个插槽开始,并且是,因此读取器从插槽 0 开始,写入器从插槽 1 开始,然后每个写入器首先通过调用如下所示的方法对写入器序列执行原子操作,然后使用 compare_and_swap 循环std::atomic<uint64_t>来声明一个插槽要更新阅读器序列以让客户端知道此插槽可用,请参阅.fetch_add(1, std::memory_order::memory_order_acq_rel)incrementSequenceupdateSequence

 inline data_type incrementSequence() {                                                                                       
        return m_sequence.fetch_add(1,std::memory_order::memory_order_seq_cst);                                                  
    }   


void updateSequence(data_type aOld, data_type aNew) {                                                                        
        while ( !m_sequence.compare_exchange_weak(aOld, aNew, std::memory_order::memory_order_release, std::memory_order_relaxed)
            if  (sequence() < aNew)  {                                                                                           
                continue;                                                                                                        
            }                                                                                                                    
            break;                                                                                                               
        }                                                                                                                        
    }                   
 inline data_type sequence() const {                                                                                          
        return m_sequence.load(std::memory_order::memory_order_acquire);                                                         
    }       
      
4

1 回答 1

4

环形缓冲区(或一般的 FIFO——不必作为环形缓冲区实现)旨在平滑流量的突发。即使生产者可能会突然产生数据,但消费者可以处理稳定的输入流。

如果您溢出 FIFO,则意味着以下两种情况之一:

  1. 你的爆发比你计划的要大。通过增加 FIFO 大小(或使其大小动态化)来解决此问题。
  2. 你的生产者跑赢了你的消费者。通过增加用于消耗数据的资源(可能更多线程)来解决此问题。

在我看来,您目前正处于第二阶段:您的单一消费者根本不够快,无法跟上生产者的步伐。在这种情况下,唯一真正的选择是通过优化单个消费者或增加更多消费者来加速消费。

听起来也有点像您的消费者在处理数据时可能会将他们的输入数据留在 FIFO 中,因此 FIFO 中的位置一直被占用,直到消费者完成处理该输入。如果是这样,您可以通过简单地让消费者在它开始处理时从 FIFO 中删除输入数据来解决您的问题。这释放了该插槽,因此生产者可以继续将输入放入缓冲区。

还有一点:使 FIFO 大小动态化可能是个问题。问题相当简单:它可以掩盖这样一个事实,即您确实存在第二个问题,即没有必要的资源来处理消费者端的数据。

假设生产者和消费者都是线程池,平衡系统的最简单方法通常是使用固定大小的 FIFO。如果生产者开始远远领先于消费者以至于 FIFO 溢出,那么生产者开始阻塞。这让消费者线程池消耗更多计算资源(例如,在更多内核上运行)以赶上生产者。但是,这确实取决于能够添加更多消费者,而不是将系统限制为单个消费者。

于 2014-06-21T17:14:45.383 回答