Ich würde mich freuen, wenn Sie mir auf einfache Weise erklären könnten, wie das Störgeräusch funktioniert. Dieses Konzept war mir bekanntlich schwer vorstellbar.
Vielleicht könnte ich es mit Ihrer Hilfe nachvollziehen.
Ich würde mich freuen, wenn Sie mir auf einfache Weise erklären könnten, wie das Störgeräusch funktioniert. Dieses Konzept war mir bekanntlich schwer vorstellbar.
Vielleicht könnte ich es mit Ihrer Hilfe nachvollziehen.
Antworten:
Der Fowler-Artikel bietet eine gute Einführung, und diese Erklärung:
Auf einer groben Ebene können Sie sich einen Disruptor als ein Multicast-Diagramm von Warteschlangen vorstellen, in dem Produzenten Objekte ablegen, die über separate nachgelagerte Warteschlangen an alle Verbraucher zum parallelen Verbrauch gesendet werden. Wenn Sie hineinschauen, sehen Sie, dass dieses Netzwerk von Warteschlangen wirklich eine einzige Datenstruktur ist - ein Ringpuffer.
Jeder Produzent und Konsument hat einen Sequenzzähler, der angibt, an welchem Slot im Puffer gerade gearbeitet wird. Jeder Produzent / Konsument schreibt seinen eigenen Sequenzzähler, kann aber die Sequenzzähler der anderen lesen. Auf diese Weise kann der Produzent die Zähler der Verbraucher lesen, um sicherzustellen, dass der Steckplatz, in den er schreiben möchte, verfügbar ist, ohne dass die Zähler gesperrt sind. Ebenso kann ein Verbraucher sicherstellen, dass er Nachrichten nur dann verarbeitet, wenn ein anderer Verbraucher damit fertig ist, indem er die Zähler überwacht.
Ein konventionellerer Ansatz könnte eine Producer-Warteschlange und eine Consumer-Warteschlange verwenden, die jeweils Sperren als Parallelitätsmechanismen verwenden. In der Praxis kommt es bei Produzenten- und Konsumentenwarteschlangen dazu, dass die Warteschlangen die meiste Zeit entweder vollständig leer oder voll sind, was zu Sperrenkonflikten und verschwendeten Taktzyklen führt. Der Disruptor mildert dies teilweise dadurch, dass alle Produzenten und Konsumenten denselben Warteschlangenmechanismus verwenden und sich durch Beobachtung der Sequenzzähler aufeinander abstimmen, anstatt Sperrmechanismen zu verwenden.
Aus diesem Artikel über CoralQueue :
Das Disruptormuster ist eine Batch-Warteschlange, die durch ein kreisförmiges Array (dh den Ringpuffer) gesichert ist, das mit vorab zugewiesenen Übertragungsobjekten gefüllt ist. Dabei werden Speicherbarrieren verwendet, um Produzenten und Konsumenten über Sequenzen zu synchronisieren.
Produzenten und Konsumenten treten also nicht innerhalb der kreisförmigen Anordnung aufeinander, indem sie ihre entsprechenden Sequenzen überprüfen . Und um ihre Sequenzen miteinander zu kommunizieren, verwenden sie Speicherbarrieren anstelle von Sperren. Dies ist die schnellste Art und Weise, ohne Sperren zu kommunizieren.
Glücklicherweise müssen Sie nicht auf die internen Details des Disruptormusters zurückgreifen, um es zu verwenden. Neben der LMAX-Implementierung gibt es CoralQueue, das von Coral Blocks entwickelt wurde und mit dem ich verbunden bin. Einige Leute finden es einfacher, ein Konzept durch Lesen von Code zu verstehen. Im Folgenden finden Sie ein einfaches Beispiel für einen einzelnen Produzenten, der Nachrichten an einen einzelnen Verbraucher sendet. Sie können diese Frage auch für ein Beispiel eines Demultiplexers (ein Produzent zu mehreren Konsumenten) prüfen .
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();
}
}
Haftungsausschluss: Ich bin einer der Entwickler von CoralQueue.