【问题标题】:LMAX Disruptor: Must EventHandler clone object received from EventHandler#onEventLMAX Disruptor:必须 EventHandler 克隆从 EventHandler#onEvent 接收的对象
【发布时间】:2013-08-18 14:05:00
【问题描述】:

我有一个包含许多生产者和消费者的应用程序。

据我了解,RingBuffer 在 RingBuffer init 开始时创建对象,然后当您在 Ring 中发布时复制对象并在 EventHandler 中从中获取它们。

我的应用程序 LogHandler 将接收到的事件缓存在一个列表中,以便在列表达到一定大小后以批处理模式进一步发送它。因此 EventHandler#onEvent 将接收到的对象放入列表中,一旦达到大小,它就会在 RMI 中将其发送到服务器并清除它。

我的问题是,我是否需要在放入列表之前克隆对象,据我所知,一旦使用它们就可以重复使用?

我需要在 EventHandler#onEvent 中同步对列表的访问吗?

【问题讨论】:

    标签: disruptor-pattern lmax


    【解决方案1】:

    是的 - 您的理解是正确的。您将值复制到环形缓冲区插槽中或从中复制出来。

    我建议是的,当您将值从环形缓冲区中提取到您的事件处理程序列表中时,您可以克隆这些值;否则插槽可以重复使用。

    只要列表是事件处理程序的私有成员变量并且每个线程只有一个事件处理程序实例,您就不需要同步对列表的访问。如果您有多个事件处理程序添加到同一个(例如静态)List 实例,那么您将需要同步。


    澄清:

    请务必阅读下面 OzgurH 的 cmets 中的背景。如果您坚持在中断器上使用 endOfBatch 标志并使用它来决定批次的大小,则不必将对象复制到列表之外。如果您使用自己的累积策略(例如大小 - 根据问题),那么您应该克隆对象,因为插槽可以在您有机会发送之前重复使用。

    另外值得注意的是,如果您需要在列表实例上进行同步,那么您已经错过了使用破坏者的大好机会,并且无论如何都会破坏您的性能。

    【讨论】:

    • Re: “我建议是的,你克隆值,因为你从环形缓冲区中提取它并进入你的事件处理程序列表;否则可以重用插槽”句子在您的回答中,请您看一下@Michael Barker 的this answer,以及我最近对此的评论。它并不完全适用于 OP 的执行流程,但是如果在使用 endOfBatch == true 从事件处理程序返回之前处理同批事件,则不需要克隆事件数据,对吧?
    • 是的@OzgurH,这是一个很好的观点。你是对的,你可以等到 endOfBatch 释放对象,我不知道这种行为应该是合同的一部分,所以很高兴知道。我已经通过澄清更新了回复。我仍然认为对于原始问题,他们应该打算复制出对象,因为问题上下文包括按大小进行批处理(我认为这意味着他们将忽略 endOfBatch 并使用 List 的长度来决定何时发送)。
    【解决方案2】:

    可以在 Disruptor 的 RingBuffer 中使用插槽(包括包含 List 的插槽)而无需克隆/复制值。这可能是一个更适合您的解决方案,具体取决于您是否担心垃圾创建,以及您是否真的需要关注对放置在 RingBuffer 中的对象的并发更新。如果放置在插槽列表中的所有对象都是不可变的,或者如果它们一次只被一个线程更新/读取(Disruptor 经常用来强制执行的前提条件),那么克隆它们不会有任何收获因为他们已经不受数据竞争的影响了。

    关于批处理,请注意,Disruptor 框架本身提供了一种机制,可以在 EventHandler 线程中从 RingBuffer 中批量获取项目。这种方法是完全线程安全且无锁的,并且可以通过使 CPU 更可预测内存访问模式来产生更好的性能。

    【讨论】:

    • 这不会扼杀提高破坏者性能的可预测的进步吗?
    猜你喜欢
    • 1970-01-01
    • 2013-12-21
    • 1970-01-01
    • 2013-11-23
    • 1970-01-01
    • 2013-05-28
    • 2012-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多