web-dev-qa-db-fra.com

Quelqu'un peut-il expliquer en termes simples quel est le modèle de perturbateur?

J'aimerais que vous m'expliquiez simplement comment fonctionne le tapoteur perturbateur. Ce concept m’a échappé comme je le sais.

Peut-être qu'avec votre aide, je pourrais le comprendre.

35
chrisapotek

Les fournisseurs Fowler Article constituent une bonne introduction, et cette explication:

À un niveau brut, vous pouvez considérer un disrupteur comme un graphique de multidiffusion de files d'attente où les producteurs y placent des objets qui sont envoyés à tous les consommateurs pour une consommation parallèle via des files d'attente en aval distinctes. Lorsque vous regardez à l'intérieur, vous voyez que ce réseau de files d'attente est vraiment une structure de données unique - un tampon en anneau.

Chaque producteur et consommateur a un compteur de séquence pour indiquer sur quel emplacement du tampon il travaille actuellement. Chaque producteur/consommateur écrit son propre compteur de séquences mais peut lire les autres compteurs de séquences. De cette façon, le producteur peut lire les compteurs des consommateurs pour s'assurer que la fente dans laquelle il veut écrire est disponible sans verrou sur les compteurs. De même, un consommateur peut s'assurer qu'il ne traite les messages qu'une fois qu'un autre consommateur en a fini en regardant les compteurs.

enter image description here

Une approche plus conventionnelle pourrait utiliser une file d'attente de producteur et une file d'attente de consommateur, chacune utilisant des verrous comme mécanismes de concurrence. Dans la pratique, ce qui se passe avec les files d'attente des producteurs et des consommateurs est que les files d'attente sont soit complètement vides, soit complètement pleines la plupart du temps, ce qui provoque des conflits de verrouillage et des cycles d'horloge perdus. Le perturbateur atténue cela, en partie, en faisant en sorte que tous les producteurs et consommateurs utilisent le même mécanisme de file d'attente, en se coordonnant les uns les autres en surveillant les compteurs de séquence plutôt qu'en utilisant des mécanismes de verrouillage.

32
Robert Harvey

De cet article sur CoralQueue :

Le modèle de perturbateur est une file d'attente de traitement par lots sauvegardée par un tableau circulaire (c'est-à-dire le tampon en anneau) rempli d'objets de transfert pré-alloués qui utilise des barrières de mémoire pour synchroniser les producteurs et les consommateurs à travers des séquences.

Ainsi, les producteurs et les consommateurs ne marchent pas l'un sur l'autre à l'intérieur du réseau circulaire en vérifiant leurs séquences correspondantes . Et pour communiquer leurs séquences d'avant en arrière, ils utilisent des barrières de mémoire au lieu de verrous. C'est le moyen sans verrou le plus rapide pour communiquer.

Heureusement, vous n'avez pas besoin de vous familiariser avec les détails internes du modèle de perturbateur pour l'utiliser. Outre l'implémentation LMAX, il y a CoralQueue développé par Coral Blocks, auquel je suis affilié. Certaines personnes trouvent qu'il est plus facile de comprendre un concept en lisant du code, donc ci-dessous est un exemple simple d'un seul producteur envoyant des messages à un seul consommateur. Vous pouvez également vérifier cette question pour un exemple de démultiplexeur (un producteur pour de nombreux consommateurs).

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

Avertissement: Je suis l'un des développeurs de CoralQueue.

9
rdalmeida