妨害者のパターンがどのように機能するかを簡単な方法で説明していただければ幸いです。この概念は、私が知る限り、とらえどころのないものでした。
おそらくあなたの助けがあれば私はそれを理解することができました。
Fowler Article は優れた入門書であり、この説明は次のとおりです。
大まかに言うと、Disruptorはキューのマルチキャストグラフと考えることができます。プロデューサーは、別のダウンストリームキューを介して並列消費するためにすべてのコンシューマーに送信されるオブジェクトを配置します。内部を見ると、このキューのネットワークが実際には単一のデータ構造-リングバッファーであることがわかります。
各プロデューサーとコンシューマーには、現在作業中のバッファー内のスロットを示すシーケンスカウンターがあります。各プロデューサー/コンシューマーは独自のシーケンスカウンターを書き込みますが、他のシーケンスカウンターを読み取ることができます。このようにして、プロデューサーはコンシューマーのカウンターを読み取り、書き込みたいスロットがカウンターをロックせずに利用できることを確認できます。同様に、コンシューマーは、カウンターを監視することにより、別のコンシューマーがメッセージを処理した後でのみメッセージを処理することを保証できます。
より一般的なアプローチでは、プロデューサーキューとコンシューマーキューを使用し、それぞれ並行処理メカニズムとしてロックを使用します。実際には、プロデューサーキューとコンシューマーキューで何が発生するかというと、ほとんどの場合、キューは完全に空か完全にいっぱいになり、ロックの競合と無駄なクロックサイクルが発生します。ディスラプタは、プロデューサとコンシューマのすべてが同じキューメカニズムを使用し、ロックメカニズムを使用するのではなくシーケンスカウンタを監視することで相互に調整することにより、これをある程度軽減します。
CoralQueue に関するこの記事から:
かく乱パターンは、メモリバリアを使用してプロデューサーとコンシューマーをシーケンスで同期させる、事前に割り当てられた転送オブジェクトで満たされた循環配列(つまり、リングバッファー)によってバックアップされるバッチキューです。
したがって、プロデューサーとコンシューマーは循環配列内で互いに踏みつけません対応するシーケンスをチェックすることにより。そして、シーケンスを相互にやり取りするために、ロックの代わりにmemory-barriersを使用します。それは彼らが通信できる最速のロックフリー方法です。
幸いなことに、それを使用するために妨害パターンの内部の詳細に取り掛かる必要はありません。 LMAXの実装の他に、私が所属するCoral Blocksによって開発された CoralQueue があります。一部の人々はコードを読むことで概念を理解する方が簡単だと思うので、以下は単一のプロデューサーが単一のコンシューマーにメッセージを送信する単純な例です。 この質問 をチェックして、デマルチプレクサ(1つのプロデューサから多くのコンシューマ)の例を確認することもできます。
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人です。