在LMAX破坏者模式中,你如何处理缓慢的消费者?

时间:2014-06-21 17:03:03

标签: c++ multithreading disruptor-pattern lmax

我有一个问题,如果在像lmax破坏者这样的慢速消费者的情况下该怎么做,这个破坏者有多个生产者和一个在x86 Linux上运行的消费者。使用类似环形缓冲区模式的lmax,您不断地覆盖数据,但如果消费者速度慢,那该怎么办呢。因此,如何处理10个大小的环形缓冲区0-9环形插槽,消费者在插槽5上的情况,现在您的编写器已准备好开始写入插槽15,这也是缓冲区中的插槽5(即:插槽) 5 = 15%10)?处理这种情况的典型方法是什么,以便编写者仍然按顺序生成数据,客户端将以相同的顺序接收数据?那真的是我的问题。下面是我的设计的一些细节,它工作正常,只是我目前没有一个很好的方法来处理这个问题。有多个线程正在进行写操作,而一个线程正在进行读取我不能在不改变现有设计的情况下引入多个读取器线程,这超出了当前的项目范围,但如果他们将此作为解决方案,仍然感兴趣。

设计细节

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

代码在x86 Linux上运行,有多个线程为编写器运行,并且读者只有一个线程。读取器和写入器分开一个插槽并且是std::atomic<uint64_t>,因此读取器从插槽0开始,插槽1处的写入器然后每个写入器首先通过在写入器序列上执行原子fetch_add(1, std::memory_order::memory_order_acq_rel)来声明插槽通过调用下面显示的incrementSequence,然后使用compare_and_swap循环更新阅读器序列,让客户知道此插槽可用,请参阅updateSequence

 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);                                                         
    }       

1 个答案:

答案 0 :(得分:4)

环形缓冲区(或通常的FIFO - 不必实现为环形缓冲区)旨在消除突发流量。即使生产者可以突发产生数据,消费者也可以处理稳定的输入流量。

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

  1. 你的爆发比你计划的要大。通过增加FIFO大小(或使其大小动态)来解决此问题。
  2. 您的生产商正在超越您的消费者。通过增加用于使用数据的资源(可能更多的线程)来解决这个问题。
  3. 听起来我觉得你正在打第二个:你的单身消费者根本不足以跟上生产者的步伐。在这种情况下,唯一真正的选择是通过优化单个消费者或增加更多消费者来加速消费。

    它也听起来,好像你的消费者在处理数据时可能会将他们的输入数据留在FIFO中,这样FIFO中的点仍然被占用,直到消费者完成< / em>处理该输入。如果是这样,您可以通过让消费者在开始处理后立即从FIFO中删除输入数据来解决您的问题。这释放了该槽,因此生产者可以继续将输入放入缓冲区。

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

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