Qt Eigene Daten per Signal-Slot effizient übertragen



  • Hallo,

    angenommen ich habe eine Datenstruktur wie zum Beispiel:

    struct data
    {
        int value_1;
        int value_2;
        int value_3;
    };
    Q_DECLARE_METATYPE(data);
    

    Dazu hab ich jetzt mehrere Background Threads (Klassen die von QThread erben), welche sich in einem ThreadManager befinden, etwa so:

    struct Thread_1 : public QThread
    {
        Q_OBJECT
    }; // etc.
    
    struct ThreadManager : public QThread
    {
        Q_OBJECT
        Thread_1 t1;
        Thread_2 t2; // etc.
    }
    

    Dabei haben die Threads unterschiedliche Aufgaben, benötigen aber alle die Daten aus data . Globale Variablen will ich vermeiden und das Ganze am liebsten per Signals/Slots lösen. Prinzipiell geht das auch ganz gut.

    Mein Problem ist jetzt, wie ich das mit den geteilten Daten am besten mache. Ich dachte mir das so dass jeder Thread ein data member hat, mit dem er arbeiten kann. Wenn einer der Threads die Daten ändern will, signalisiert er diese Änderung dem ThreadManager. Dieser ändert seine Daten entsprechend und teilt dann wiederum per Signal allen anderen Threads die neuen Daten mit.

    Also schematisch etwa so:

    Thread_1:         SIGNAL  Änderung von Thread_1::data gewünscht // *
    ThreadManager:    SLOT    Änderungswunsch aktzeptiert, ThreadManager::data wird geändert
    ThreadManager:    SIGNAL  Änderung der verteilten Daten signalisieren
    Thread_1 bis n:   SLOT    Empfangen der neuen Daten, Synchronisierung der Daten in allen n Threads
    

    Soweit die Vorüberlegungen, ist das so in Ordnung? Oder hab ich hier schon einen Fehler drin bzw. kann man das besser machen?

    Wenn nicht ist jetzt mein Hauptproblem, wie ich effizient das erste Signal (oben mit einem "*" markiert) absende. Sagen wir mein Thread sieht etwa so aus:

    struct Thread_1 : public QThread
    {
        Q_OBJECT
        data m_data;
    
    public:
        void doSomething()
        {
            m_data.value_1 = 3; // Problem: Signal wird hier nicht versendet!
        }    
    
    signals:
        // Änderungswunsch an ThreadManager
        void dataChangeRequested(data const &d);
    public slots:
        // Synchronisierte, neue Daten von ThreadManager
        void dataChanged(data const &d) { m_data = d; }
    };
    

    Hier habe ich jetzt das Problem, dass wenn ich die Daten m_data verändere, dass dadurch nicht automatisch das Änderungssignal versendet wird. Der Benutzer muss also daran denken nach jeder Änderung selbst ein emit dataChangeRequested(m_data); zu senden.

    Das würde ich gerne vermeiden bzw. automatisieren, so dass sich der Benutzer der Klasse Thread_1 nicht darum kümmern muss das Signal zu senden, sondern dass dieses automatisch beim Verändern der Daten m_data gesendet wird.

    Ich dachte mir zuerst dass m_data über setter selbst ein Signal senden könnte - Problem ist dabei aber dass es dazu von QObject erben müsste, was dann den Copy-Contructor deleted und nicht mehr mit Q_DECLARE_METATYPE benutzt werden kann...

    Wie löst man dieses Problem am besten in Qt? Müsste ja eigentlich relativ häufig vorkommen so etwas...



  • Dazu hab ich jetzt mehrere Background Threads (Klassen die von QThread erben), welche sich in einem ThreadManager befinden, etwa so:

    Laut Namen und beschreibung würd ich davon ausgehen, das dein ThreadManager Threads Managed, und sich nicht selber verhält wie einer und auch als Thread verwendet werden kann ?

    Warum ist also ThreadManager von QThread abgeleitet ?

    Schau dir mal QThreadpool an ...

    Der Benutzer muss also daran denken nach jeder Änderung selbst ein emit dataChangeRequested(m_data); zu senden.
    Das würde ich gerne vermeiden bzw. automatisieren,

    Dein Komfortdenken in Ehren, aber die Frage ist, ob Du Dir da nicht mehr Ärger einhandels als es wert ist ....
    Abgesehen vom "informieren" hasst du nen ähnliches Problem mit dem Locken auch, wenn das nicht nur durch die info Logik schon abfangen willst.

    - wie selbst bemerkt ... deine "Daten" müssten mit der Threadimpelentierung "verheiratet" werden. kritisch fürs Abhängigkeitsmanagment.

    oder du wrappst deine Datenklasse reimplementierst alle Zugriffsfunktionen mit Callback / Interface generisch, so daß da später von aussen Locken / Informieren kannst.

    Und bei dir ist dann zwingend jede einzelne operation eine Info auslösen und wahrscheinlich zu einer Aktion führen. Zusammenfassen von mehreren Ops auf dem Object und einmal die Aktion anstossen, wuerde entweder dein System unterlaufen, oder du müsstest zig Methoden definieren und schreiben für jede zusammengesetzte Op .... auch doof.

    emit dataChangeRequested(m_data);

    ist gar ned so unüblich (siehe modell view )

    Ciao ...



  • RHBaum schrieb:

    Laut Namen und beschreibung würd ich davon ausgehen, das dein ThreadManager Threads Managed, und sich nicht selber verhält wie einer und auch als Thread verwendet werden kann ?

    Warum ist also ThreadManager von QThread abgeleitet ?

    Schau dir mal QThreadpool an ...

    Ups, Copy-Paste Fehler, der ThreadManager sollte von QObject abgeleitet sein.

    RHBaum schrieb:

    Dein Komfortdenken in Ehren, aber die Frage ist, ob Du Dir da nicht mehr Ärger einhandels als es wert ist ....
    Abgesehen vom "informieren" hasst du nen ähnliches Problem mit dem Locken auch, wenn das nicht nur durch die info Logik schon abfangen willst.

    - wie selbst bemerkt ... deine "Daten" müssten mit der Threadimpelentierung "verheiratet" werden. kritisch fürs Abhängigkeitsmanagment.

    oder du wrappst deine Datenklasse reimplementierst alle Zugriffsfunktionen mit Callback / Interface generisch, so daß da später von aussen Locken / Informieren kannst.

    Ok, Danke schonmal für die Antwort, werd mir mal in Ruhe überlegen wie ich das dann mache... Vielleicht bleibts auch einfach beim "manuellen" synchronisieren, das funktioniert zumindest solange es nicht vergessen wird.



  • Wenn es Dir wirklich so wichtig ist, das du aufwand spendieren willst ....
    Dann baust eh deine Datenklasse ohne irgendwelche Threads und Benachrichtigungs Dinge mit schoen sauberen settern und gettern.

    Dann kapselst das ding ( Designpattern: Facade / Proxy) also mit gleichen Interface, aber als QT Klasse.

    Und mit der machst dann die Thread-Synchronisation und die kannst auch in ne Qt Queue reinhaengen, also signale/slots verwenden.
    In deiner QT Implementation verwendest dann natürlich den Qt Wrapper.

    Nachteil: ändern sich die Zugriffsfunktionen deiner Datenklasse, musst natürlich auch die die Qt (Proxy)Klasse nachziehen ...

    Ciao ...


Anmelden zum Antworten