有人可以简单地解释什么是破坏者模式吗?


Answers:


33

福勒条供应商良好的引物,和这样的解释:

粗略地讲,您可以将Disruptor视为队列的多播图,生产者将其放在对象上,然后通过单独的下游队列发送给所有使用者以进行并行使用的对象。当您查看内部时,您会发现该队列网络实际上是单个数据结构-环形缓冲区。

每个生产者和使用者都有一个序列计数器,以指示其当前正在缓冲区中的哪个插槽。每个生产者/消费者都编写自己的序列计数器,但可以读取其他人的序列计数器。这样,生产者可以读取消费者的计数器,以确保要写入的插槽可用,而计数器没有任何锁定。同样,使用者可以通过观察计数器来确保仅在其他使用者完成处理消息后才处理消息。

在此处输入图片说明

较常规的方法可能使用生产者队列和消费者队列,每个都使用锁作为并发机制。在实践中,生产者和使用者队列发生的情况是,大多数时间队列要么完全为空,要么完全充满,这会导致锁争用并浪费时钟周期。破坏者部分地通过使所有生产者和消费者使用相同的队列机制来缓解这种情况,通过观察序列计数器而不是使用锁定机制来相互协调。


9

从这篇有关CoralQueue的文章中:

破坏者模式是一个批处理队列,该批处理队列由填充有预分配传输对象的环形阵列(即环形缓冲区)支持,该环形对象使用内存屏障通过序列同步生产者和消费者。

因此,生产者和消费者不会通过检查它们相应的序列而在圆形数组内互相踩踏。为了彼此之间来回传递序列,它们使用内存屏障而不是锁。这是他们交流的最快的无锁方式。

幸运的是,您无需深入了解干扰模式的内部细节即可使用它。除了LMAX实现之外,还有我隶属于Coral Blocks开发的CoralQueue。某些人发现通过阅读代码更容易理解一个概念,因此以下是单个生产者向单个消费者发送消息的简单示例。您也可以查看此问题的解复用器(一个生产者对许多消费者)示例。

package com.coralblocks.coralqueue.sample.queue;

import com.coralblocks.coralqueue.AtomicQueue;
import com.coralblocks.coralqueue.Queue;
import com.coralblocks.coralqueue.util.Builder;

public class Basics {

    public static void main(String[] args) {

        final Queue<StringBuilder> queue = new AtomicQueue<StringBuilder>(1024, new Builder<StringBuilder>() {
            @Override
            public StringBuilder newInstance() {
                return new StringBuilder(1024);
            }
        });

        Thread producer = new Thread(new Runnable() {

            private final StringBuilder getStringBuilder() {
                StringBuilder sb;
                while((sb = queue.nextToDispatch()) == null) {
                    // queue can be full if the size of the queue
                    // is small and/or the consumer is too slow

                    // busy spin (you can also use a wait strategy instead)
                }
                return sb;
            }

            @Override
            public void run() {

                StringBuilder sb;

                while(true) { // the main loop of the thread

                    // (...) do whatever you have to do here...

                    // and whenever you want to send a message to
                    // the other thread you can just do:
                    sb = getStringBuilder();
                    sb.setLength(0);
                    sb.append("Hello!");
                    queue.flush();

                    // you can also send in batches to increase throughput:
                    sb = getStringBuilder();
                    sb.setLength(0);
                    sb.append("Hi!");

                    sb = getStringBuilder();
                    sb.setLength(0);
                    sb.append("Hi again!");

                    queue.flush(); // dispatch the two messages above...
                }
            }
        }, "Producer");

        Thread consumer = new Thread(new Runnable() {

            @Override
            public void run() {

                while (true) { // the main loop of the thread

                    // (...) do whatever you have to do here...

                    // and whenever you want to check if the producer
                    // has sent a message you just do:

                    long avail;
                    while((avail = queue.availableToPoll()) == 0) {
                        // queue can be empty!
                        // busy spin (you can also use a wait strategy instead)
                    }

                    for(int i = 0; i < avail; i++) {
                        StringBuilder sb = queue.poll();
                        // (...) do whatever you want to do with the data
                        // just don't call toString() to create garbage...
                        // copy byte-by-byte instead...
                    }
                    queue.donePolling();
                }
            }
        }, "Consumer");

        consumer.start();
        producer.start();
    }
}

免责声明:我是CoralQueue的开发人员之一。


1
声明您与所描述软件的隶属关系将非常好。
Deer Hunter
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.