wie organisiere ich meine Threads beim Empfang von Telegramen vom Feldbus



  • Hallo alle,

    wir planen ein Modul in unserer Software neu zu designen, welches für den Empfang von CAN-Telegrammen zuständig ist. Die Telegramme kommen mit einer Frequenz von bis zu 3000 pro Sekunde rein (meistens ca. 1000 1/s). Die Größe eines Telegramms ist vernachlässigbar klein.
    Es sind ca. 80 Objekte in unserer Applikation, die eines dieser Telegramme empfangen. Es gibt auch Telegramme, die an mehr als ein Objekt addressiert sind.

    Aus Gründen der verwendeten Treiber brauchen wir mindesten einen Thread pro Kanal, wobei es zwei oder vier Kanäle geben kann. Aus diesem Thread heraus wird eine blockierende Funktion aufgerufen, die auf ein Telegramm wartet. Bisher hatte jedes der Objekte seinen eigenen Emfangs-Thread, was ein paar Nachteile hat.
    Das ganze läuft geschrieben in C++ unter Windows 7 meistens auf Quad-Core-PCs. boost.thread verwenden wir schon, boost.asio wäre eine Option.

    Ich habe zwei grundsätzlich verschiedene Ideen, wie ich die Threads in der Applikation organisiere; bevor ich das hier breit trete, Frage an Euch:
    wie organisiert man in so einer Anwendung die Threads 'am besten'?

    Die selbe Frage habe ich auf Stackoverflow gestellt: http://stackoverflow.com/questions/27543946/receiving-messages-with-high-frequence-asking-for-the-best-thread-model

    Gruß
    Werner



  • IMHO sollte man threads nach Möglichkeit gar nicht selbst verwalten. Ich finde den Ansatz von https://www.threadingbuildingblocks.org/ interessant. Für deinen Zweck könnte der Flow Graph nützlich sein.
    Ich arbeite mich allerdings auch erst in TBB ein, daher habe ich bisher leider kaum Erfahrung damit.



  • Werner Salomon schrieb:

    Aus Gründen der verwendeten Treiber brauchen wir mindesten einen Thread pro Kanal, wobei es zwei oder vier Kanäle geben kann.

    Dann würde ich das vermutlich genau so machen, also ein Thread pro Kanal.

    Und dann kommts auf die Anforderungen der Anwendung drauf an. Es kann Sinn machen die Telegramme gleich synchron im Empfangs-Thread zu verarbeiten. Es kann Sinn machen sie über eine Producer-Consumer Queue an einen einzigen "verarbeite alles" Thread weiterzugeben.
    Oder an mehrere Threads die verarbeiten, z.B. einen pro Objekt.

    Kommt halt drauf an. z.B. darauf wie lange so einer Verarbeitung dauern kann, ob es wichtig ist dass so schnell wie möglich wieder die Empfangs-Funktion des Treibers aufgerufen wird, trotz dem die davor empfangenen Telegramme vielleicht noch gar nicht fertig verarbeitet wurden etc. Ob andere blockierende Funktionen aufgerufen werden müssen um die Nachrichten zu verarbeiten.

    Bzw. bei Messages die an mehrere Objekte geschickt werden - muss da sichergestellt sein dass Objekt A erst mit der Verarbeitung von Nachricht 2 anfangen darf, nachdem sichergestellt ist dass Objekt A und B beide Nachricht 1 vollständig abgearbeitet haben.

    Und natürlich die Frage wie viel der "Arbeit" daraus besteht mit "shared state" rumzumachen - also wie oft bzw. wie lange man zum Bearbeiten einer Nachricht "in" einem Objekt eine globale Mutex locken müsste.

    Grundsätzlich kann die Empfangs-Schleife aber vermutlich einfach so aussehen (plus Error-Handling, Shutdown-Handling etc. natürlich):

    for (;;)
        {
            auto tel = m_device.ReceiveTelegram();
            m_dispatcher.Push(tel);
        }
    

    Wie's weiter geht ist dem Programmteil dann egal, das ist dann die Aufgabe des Dispatchers.
    Also ob der die Verarbeitung direkt synchron in Push macht, eine gemeinsame Producer-Consumer Queue verwendet, eine Producer-Consumer Queue pro "Empfänger" usw.



  • Aus Gründen der verwendeten Treiber brauchen wir mindesten einen Thread pro >Kanal, wobei es zwei oder vier Kanäle geben kann.

    d.h. dein treiber hat nur blockierende funktionen? ganz ganz sicher?

    Aus diesem Thread heraus wird eine blockierende
    Funktion aufgerufen, die auf ein Telegramm wartet.

    warum blockiert die? wenn du die Raten nicht schaffst bringt dir
    ein absichern doch auch nichts mehr - oder?

    oder meinst du n Threads auf 1 blockierende

    Bisher hatte jedes der Objekte seinen eigenen Emfangs-Thread, was ein paar >Nachteile hat.

    wir brauchen mehr Details zu den "logischen" CAN-Inhalten - sonst versteht hier keiner woviele Objekte/Threads auf wieviel warten

    nur empfangen - oder auch permantent/sporadisch senden?

    boost.asio wäre eine Option.

    wofür asio?



  • Die selbe Frage habe ich auf Stackoverflow gestellt

    auf stackoverflow steht ein paar mehr Details - so viel zu "Die selbe"



  • Die Größe eines Telegramms ist vernachlässigbar klein.
    Es sind ca. 80 Objekte in unserer Applikation,
    die eines dieser Telegramme empfangen. Es gibt auch Telegramme, die an mehr als >ein Objekt addressiert sind.

    Tip 1:

    d.h. du kannst problemlos mit einem statische Puffer arbeiten
    z.B. einem Ringpuffer - keine Allokation = mehr Durchsatz = mehr Zeit fuer andere Dinge

    und dann die Pakete per Referenz (ohne Allok und Copy) aus dem Ringpuffer zur Bearbeitung an die Objekte verteilen

    oder geht aus ungenannten Gründen nicht?

    Tip 2:

    wie werde die Pakete den Objekten zugeordnet - per Id oder sowas? da auch unbeding auf die Performanz achten - einen sortierten std::vector mit den Id+Objekt-Ptr koennte bei der Menge viel schneller sein als per std::(unordered)map an den Objekt-Pointer zu kommen

    vernachlässige auch nicht die Anzahl an trivial virtuellen-Methoden aufrufen (und deren Dauer) - Falls das relevant ist

    Tip 3:

    wenn die Objekte keinen eigenen Kontext brauchen - oder der abgetrennt nur fuer andere Sachen da ist würde ich darauf verzichten die Objekte mit Qeues auszustatten

    - oder brauchst du Empfangs-Queues?

    wie gesagt: zu wenig Infos für eine gute Beurteilung



  • Tip4:

    und du solltest dir eine Möglichkeit überlegen wie du den Durchsatz (ohne Mallocs usw) messbar machst - es wäre sicher auf Dauer (per #define aktivierbar) schön zu wissen ob noch viel Luft da ist oder ob der Durchsatz so knapp gehalten wird



  • Die Telegramme kommen mit einer Frequenz von bis zu 3000 pro Sekunde rein (meistens ca. 1000 1/s). Die Größe eines Telegramms ist vernachlässigbar klein.

    Gibt es eigentlich einen Treiber für den CAN Bus, welcher ähnlich dem seriellen Port, die Daten empfängt und in einen Puffer steckt? Und wie groß ist der Puffer?

    Und wieviel Datendurchsatz ist zu erwarten? Kann ich davon ausgehen dass jeder Kanal sagen wir mal 4 kByte Daten alle 5 Minuten liefert? Läuft der Puffer des Treibers voll?

    3000 Pakete pro Sekunde ist eine echt stramme Angelegenheit. Und wenn das Ding mehrere Megabyte alle 2 Minuten sendet, hast du ein Problem.

    Stell dir mal vor es kommt ein Burst von 6000 Paketen rein. Jedes Paket belegt 4 Bytes, macht zusammen 24000 Bytes. Danach ist 50ms Ruhe, worauf wieder so ein Burst reinkommt. -> Deine Empfangsfunktion muss 24000Byte alle 50ms verarbeiten. Und dann kommt Windows, will den Suchindex und Virenscanner aktualisieren und verbrät mal 1 Sekunde, s.d. in der Zwischenzeit dein Empfangspuffer überläuft.


Anmelden zum Antworten