Ich bin dabei, ein System zu entwerfen, das eine Verbindung zu einem oder mehreren Datenfeeds herstellt und eine Analyse der Daten durchführt, um Ereignisse basierend auf dem Ergebnis auszulösen. In einem typischen Multithread-Producer / Consumer-Setup werden mehrere Producer-Threads Daten in eine Warteschlange stellen und mehrere Consumer-Threads die Daten lesen. Die Konsumenten interessieren sich nur für den neuesten Datenpunkt plus n Anzahl von Punkten. Die Producer-Threads müssen blockieren, wenn der langsame Consumer nicht mithalten kann, und natürlich werden die Consumer-Threads blockiert, wenn keine unverarbeiteten Updates vorhanden sind. Die Verwendung einer typischen gleichzeitigen Warteschlange mit Lese- / Schreibsperre funktioniert gut, aber die eingehende Datenrate kann sehr hoch sein. Daher wollte ich meinen Sperraufwand reduzieren, insbesondere die Schreibsperren für die Produzenten. Ich denke, ein kreisförmiger, sperrenfreier Puffer ist das, was ich brauchte.
Nun zwei Fragen:
Ist ein kreisförmiger sperrenfreier Puffer die Antwort?
Wenn ja, bevor ich meine eigenen rolle, kennen Sie eine öffentliche Implementierung, die meinen Anforderungen entspricht?
Hinweise zur Implementierung eines zirkularen sperrenfreien Puffers sind immer willkommen.
Übrigens, dies in C ++ unter Linux.
Einige zusätzliche Informationen:
Die Reaktionszeit ist für mein System entscheidend. Im Idealfall möchten die Consumer-Threads, dass Aktualisierungen so schnell wie möglich eingehen, da eine zusätzliche Verzögerung von 1 Millisekunde das System wertlos machen oder viel weniger wert sein kann.
Die Designidee, zu der ich mich neige, ist ein halbverriegelungsfreier Ringpuffer, in dem der Producer-Thread Daten so schnell wie möglich in den Puffer legt. Rufen wir den Kopf des Puffers A auf, ohne ihn zu blockieren, es sei denn, der Puffer ist voll, wenn A trifft auf das Ende des Puffers Z. Consumer-Threads enthalten jeweils zwei Zeiger auf den Kreispuffer P und P n , wobei P der lokale Pufferkopf des Threads ist und P n das n-te Element nach P ist. Jeder Consumer-Thread rückt sein P vor und P n, sobald es die Verarbeitung des Stroms P beendet hat und das Ende des Pufferzeigers Z mit dem langsamsten P n vorgerückt ist. Wenn P A erreicht, was bedeutet, dass kein neues Update mehr verarbeitet werden muss, dreht sich der Verbraucher und wartet damit, dass A wieder vorrückt. Wenn sich der Consumer-Thread zu lange dreht, kann er in den Ruhezustand versetzt werden und auf eine Bedingungsvariable warten, aber ich bin damit einverstanden, dass der Consumer den CPU-Zyklus in Anspruch nimmt und auf ein Update wartet, da dies meine Latenz nicht erhöht (ich werde mehr CPU-Kerne haben als Fäden). Stellen Sie sich vor, Sie haben eine kreisförmige Spur und der Produzent läuft vor einer Gruppe von Verbrauchern. Der Schlüssel besteht darin, das System so abzustimmen, dass der Produzent normalerweise nur ein paar Schritte vor den Verbrauchern läuft, und die meisten dieser Vorgänge können es sein erfolgt mit sperrfreien Techniken. Ich verstehe, dass es nicht einfach ist, die Details der Implementierung richtig zu machen ... okay, sehr schwer, deshalb möchte ich aus den Fehlern anderer lernen, bevor ich einige meiner eigenen mache.