¿Alguien puede explicar en términos simples cuál es el patrón disruptor?

Respuestas:

33

The Fowler Article ofrece una buena introducción, y esta explicación:

A un nivel bruto, puede pensar en un disruptor como un gráfico de multidifusión de colas donde los productores colocan objetos que se envían a todos los consumidores para consumo paralelo a través de colas separadas aguas abajo. Cuando miras dentro, ves que esta red de colas es realmente una estructura de datos única: un búfer en anillo.

Cada productor y consumidor tiene un contador de secuencia para indicar en qué ranura del búfer está trabajando actualmente. Cada productor / consumidor escribe su propio contador de secuencia pero puede leer los contadores de secuencia de los demás. De esta manera, el productor puede leer los contadores de los consumidores para asegurarse de que el espacio en el que desea escribir esté disponible sin ningún bloqueo en los contadores. Del mismo modo, un consumidor puede asegurarse de que solo procese los mensajes una vez que otro consumidor haya terminado con él mirando los contadores.

ingrese la descripción de la imagen aquí

Un enfoque más convencional podría usar una cola de productor y una cola de consumidor, cada una usando bloqueos como mecanismos de concurrencia. En la práctica, lo que sucede con las colas de productores y consumidores es que las colas están completamente vacías o completamente llenas la mayor parte del tiempo, lo que provoca contención de bloqueo y ciclos de reloj desperdiciados. El disruptor alivia esto, en parte, al hacer que todos los productores y consumidores usen el mismo mecanismo de cola, coordinándose entre sí observando los contadores de secuencia en lugar de usar mecanismos de bloqueo.

Robert Harvey
fuente
9

De este artículo sobre CoralQueue :

El patrón disruptor es una cola de procesamiento por lotes respaldada por una matriz circular (es decir, la memoria intermedia de anillo) llena de objetos de transferencia preasignados que utiliza barreras de memoria para sincronizar productores y consumidores a través de secuencias.

Por lo tanto, los productores y los consumidores no se pisan entre sí dentro de la matriz circular al verificar sus secuencias correspondientes . Y para comunicar sus secuencias entre sí, utilizan barreras de memoria en lugar de bloqueos. Esa es la forma más rápida sin bloqueo que pueden comunicarse.

Afortunadamente, no necesita conocer los detalles internos del patrón disruptor para usarlo. Además de la implementación de LMAX, está CoralQueue desarrollado por Coral Blocks, con el que estoy afiliado. A algunas personas les resulta más fácil entender un concepto al leer el código, por lo que a continuación se muestra un ejemplo simple de un solo productor que envía mensajes a un solo consumidor. También puede consultar esta pregunta para ver un ejemplo de demultiplexor (un productor para muchos consumidores).

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

Descargo de responsabilidad: soy uno de los desarrolladores de CoralQueue.

rdalmeida
fuente
1
Sería bueno indicar su afiliación con el software que describe.
Deer Hunter