Wie realisiert man eine/n Hintergrundscheife / -prozess



  • Verwende einen Thread...

    Das hört sich gut an! Ich habe in der wxWidgets Hilfe was gefunden. Es gibt eine Klasse die sich wxThreadHelper nennt. Wie kann ich einen neuen Thread erstellen?



  • phlox81 schrieb:

    1. Verwende einen Timer, und lese dann vom Comport, kann aber auch deine App zum hängen bringen, ist also nicht die erste Wahl.

    Natürlich kann das passieren. Dass passiert aber auch bei jedem while(1);. Normalerweise sollte er auch nichtblockierend vom Com-Port lesen können.

    KRibel schrieb:

    Das hört sich gut an! Ich habe in der wxWidgets Hilfe was gefunden. Es gibt eine Klasse die sich wxThreadHelper nennt. Wie kann ich einen neuen Thread erstellen?

    http://www.wxwidgets.org/manuals/stable/wx_wxthreadoverview.html#wxthreadoverview
    Aber am besten du Informierst dich erstmal allgemein über Threads. Da gibt es einiges zu beachten, was am Anfang nicht so leicht ist und auch später immer wieder probleme machen wird.


  • Mod

    KRibel schrieb:

    Verwende einen Thread...

    Das hört sich gut an! Ich habe in der wxWidgets Hilfe was gefunden. Es gibt eine Klasse die sich wxThreadHelper nennt. Wie kann ich einen neuen Thread erstellen?

    Ist nicht so schwer, im wxWidgetsbuch gibt es ein Kapitel dazu.
    Im Prinzip musst du von wxThread ableiten, und die Entry Methode überschreiben.
    Aber wie ProgChild schon schreibt, du solltest dich erstmal allgemein mit Threads beschäftigen.



  • Ich bin gerade dabei das Kapitel wxThread durchzulesen.

    Ich danke euch schonmal. Ich werde bestimmt noch ein paar Startprobleme haben.



  • Also hab mir das Kapitel durchgelesen.

    Nun, hab ich das so richtig verstanden?

    Meine Klassendefinition sieht so aus:

    class meineKlasse : public wxThread
    {
        public:
            meineKlasse() : wxThread()
            {}
    
            vitual void *Entry()
    };
    

    Dazu gehören noch ein paar Atributen und andere Methoden.
    In Entry mache ich praktisch meine Endlosschleife rein, richtig?

    Die Instantiierug der Klasse sieht dan so aus:

    meineKlasse *test = new meineKlasse();
    test->Create();
    test->Run();
    

    Natürlich sollte ich noch auf mögliche Fehler abfragen, ob das Thread auch wirklich erstellt wurde usw.

    Ist das denn so richtig wie ich es verstanden habe?



  • Das ist so richtig. Jetzt musst du nur noch beachten, dass du aus deinem Thread unter gar keinen Umständen auf GUI-Funktionen zugreifen darfst. Du musst also Events an deinen Hauptthread schicken, um dein GUI upzudaten.



  • Heißt das, dass ich die eingelesenen Werte vom Thread aus nicht in Edit-Felder schreiben darf?



  • KRibel schrieb:

    Heißt das, dass ich die eingelesenen Werte vom Thread aus nicht in Edit-Felder schreiben darf?

    Ja.

    Zumindest nicht direkt. Das ist der große Nachteil von Threads. Du musst Synchronisieren usw. Das Problem ist, dass es zu Fehlern kommen kann, wenn zwei Threads auf den gleichen Datenbereich zugreifen. Da wxWidgets nicht komplett Thread-Save ist musst du das Problem so umgehen, dass du nur Funktionen benutzt, die Thread-Save sind. Also Funktionen, die darauf aufpassen, sich nicht gegenseitig in die Quere zu kommen, wenn sie aus verschiedenen Threads aufgerufen werden. Das sind die Funktionen wie, AddPendingEvents, etc.

    Du schickst also ein Event an deinen Hauptthread, dass er doch Bitte einen bestimmten Text ausgeben soll.



  • Du schickst also ein Event an deinen Hauptthread, dass er doch Bitte einen bestimmten Text ausgeben soll.

    Wie sieht denn so etwas aus?

    Muss ich bei der Instantiierung von meineKlasse ein Objekt der Hauptklasse übergeben, damit ich dann auf verschiedene Methoden zugreifen kann um den Text auszugeben?


  • Mod

    In gewisserweise ja. Was Events angeht, schau dir mal das 2. wxWidgets Tutorial im Magazin an,
    da ist auch beschrieben, wie man eigene Events anlegt. Der Thread kommuniziert dann mit der GUI über diese Events.



  • wo finde ich das 2. Tutorial? Kannst du mir da bitte ein Link geben?


  • Mod

    Ein bisschen weiter unten im Forum fürs Magazin.
    http://c-plusplus.net/forum/viewtopic-var-t-is-175682.html



  • KRibel schrieb:

    wo finde ich das 2. Tutorial? Kannst du mir da bitte ein Link geben?

    Klick mal im Forum auf: C/C++ Forum -> Das C++ Magazin: Die Artikel -> wxWidgets Tutorial Part II: Spiel mit mir
    😉

    Ansonsten schau mal in den wxWidgets Sourcen ins Verzeichnis samples/thread und da in die Datei thread.cpp. Das ist ein ziemlich ausführliches Beispiel, aber auch etwas unübersichtlich.

    Schau dir da mal genauer die Funktion *void MyWorkerThread::Entry() an. Und schau dir dann an, wie die Events im Hauptprogramm bearbeitet werden...



  • Also mit dem Tutorial bin ich nicht weiter gekommen. Ich lese mir gerade die Doku zu wxEvtHandler durch, vtl. hilft das weiter.

    Mit welcher der drei Methoden Wait(), Delete() und TestDestro() beende ich meinen Thread?

    Ich habe in meiner Entry() Methode eine Endlosschleife drin. Wenn ich dann meineKlasse->Wait() ausführe, kommt die Fehlermeldung

    "...exe hat ein Problem festgestellt und muss beendet werden."

    . Liegt es and er Endlosschleife?


  • Mod

    Poste doch deinen Code, dann sieht man das evtl. besser 😉
    Und du solltest dir das Thread example von wxWidgets anschauen.



  • Also folgendes habe ich jetzt aus der thread.cpp datei herausgefunden.

    1. Ich muss ID's definieren. Ich denke so:

    enum
    {
        WORKER_EVENT,
        ...
    };
    

    2. Ich muss eine Event Tabelle anlegen, die dann so aussieht:

    BEGIN_EVENT_TABLE( MyFrame , wxFrame )
        EVT_UPDATE(WORKER_EVENT, MyFrame::OnWorkerEvent)
        ...
    END_EVENT_TABLE()
    

    3. In der Klasse MyFrame muss ich passende Methoden anlegen.

    void OnWorkerEvent( wxCommandEvent& event )
    ...
    

    4. Mein WorkerThread rufe ich so auf:

    MyWorkerThread *worker = new MyWorkerThread( this );
    

    Ich denke, dass mit 'this' das Frame meines Programms übergeben wird.

    5. In der Methode Entry() des WorkerThreads schreibe ich folgendes rein:

    while(1)
    {
        if ( TestDestroy() )
            break;
    
        //Lese Daten vom COM-Port
    
        // Die erste Möglichkeit
        wxCommandEvent event( ich denke hier muss das rein wxEVT_COMMAND_TEXT_UPDATED , WORKER_EVENT);
        event.SetString( strng WasInDieBoxSoll );
    
        wxPostEvent( freame, event );
    
        // oder die andere Möglichkeit
        freame->WriteText(string);
    }
    

    Die Klasse MyFrame und die Klasse MyWorkerThread sind bei mir in zwei verschiedenen Header-Dateien definiert. In der Datei MyFrame.h habe ich natürlich die Headerdatei von MyWorkerThread eingebunden. So wenn ich jetzt die Headerdatei von MyFrame in die Headerdatei von MyWorkerThread einbinden möchte, geht es natürlcih nicht, weil er rekursiv versucht die andere Datei einzubinden.

    Weiß jemand wie sich das lösen lässt, ohne die Klassendefinitonen in eine Datei reinzuschreiben?


  • Mod

    Das Stichwort ist forward Declaration.

    MyThread.hpp:

    class MyFrame;
    
    class MyThread : ...
    


  • also hier mein Code:

    Headerdatei der MyWorkerThread Klasse:

    class MyFrame;
    
    class MyWorkerThread : public wxThread
    {
        private:
            MyFrame *m_frame;
            ctd9300 *CTD;
    
        public:
            MyWorkerThread( MyFrame *frame, ctd9300 *CTD_Temp );
    
            virtual void *Entry();
            virtual void OnExit();
    };
    

    Sourcecode der MyWorkerThread Klasse:

    MyWorkerThread::MyWorkerThread( MyFrame *frame, ctd9300 *CTD_Temp ) : wxThread()
    {
        m_frame = frame;
    
        CTD = CTD_Temp;
    
    }
    
    void *MyWorkerThread::Entry()
    {
        temperature temp_values;
        while( !TestDestroy() )
        {
            temp_values = CTD->getTemp();
    
            m_frame->writeValues( temp_values );
        }
    
        return NULL;
    }
    
    void MyWorkerThread::OnExit()
    {
    }
    

    und nun der Sourcecode der MyFrame Klasse:

    void MyFrame::butt_start_stopClick(wxCommandEvent& event)
    {
        if ( butt_start_stop->GetValue() )
        {
            MyWorkerThread *readTempRC = new MyWorkerThread( this ,CTD );
            readTempRC->Create();
            readTempRC->Run();
        }
        else
        {
            readTempRC->Delete();
        }
    }
    

    ctd9300 ist eine Klasse die ich zur Kommunikation via COM-Port mit meinem µC geschrieben habe. Diese Klasse funktioniert einwandfrei. Der µC sendet permanent Temperaturwerte von den angeschlossenen Sensoren (es sind mehrere). Die Methode getTemp() liefert ein Struct mit allen Temperaturwerten zurück.

    butt_start_stop ist ein ToggleButton.

    Wenn ich jetzt den dargestellten Quellcode compiliere, dann kommt die Fehlermeldung:

    invalid use of undefined type struct MyFrame' (zeile 16 Sourcecode MyWorkerThread) forward declaration ofstruct MyFrame' (zeile 1 Headerdatei MyWorkerThread)

    Würde rein theoretisch das so funktionieren?
    Was kann ich tun um diese Fehlermeldung zu beseitigen?

    Nachtrag 19.06.2007 - 14:04:
    Fehlermeldung beseitigt! Habe in die MyWorkerThread.cpp die datei MyFrame.h includiert.

    Nachtrag 19.06.2007 - 14:06:
    Beim 2. Klick auf den Button butt_start_stop bekomme ich wieder die Fehlermeldung

    MyFrame.exe hat ein Problem festgestellt und muss beendet werden.

    Ich habe das Gefühl, dass ich den Thread nicht richtig beende.
    Kann das sein?


  • Mod



  • Ja ist klar, dass Delete nur bei wxTHREAD_JOINABLE Threads funktioniert. Habe es bereits geändert. Nur bringt das nichts. Der Fehler tritt immernoch auf.


Anmelden zum Antworten