Hauptfenster wird nicht angezeigt



  • die Anzeige der Werte auf der Hauptoberfläche übernimmt ein Timer, der die messwerte alle 1 Sek aktualisiert:

    void __fastcall TForm1::timMesswerteAnzeigenTimer(TObject *Sender)
    {
       labAnzeigeTemperatur->Caption = messwerte.GetTemperatur();
       labAnzeigeDruck->Caption = messwerte.GetDruck();
       //usw...
    }
    

    Die Anwendung bleibt stehen in der WinMain bei Application->Run().



  • Das heißt, du synchronisierst da nichts, sondern liest blind Werte, ohne zu wissen, in welchem Zustand sich der Thread befindet? Das kann nicht funktionieren...

    Ich würde aus dem Thread heraus eine Botschaft senden, wenn neue Messwerte bereit liegen und dann aus dem Hauptthraed heraus die Werte auslesen. Dafür brauchst du im Thread zB eine TCriticalSection, um auszuschließen, dass du verscuhst Werte zu lesen, während die gerade vom Port gelesen werden. Oder du verwendest Synchronize() aus dem Thread heraus. Ist einfacher, hat aber auch Nachteile.

    Was soll denn das Sleep() in dem Thread? Ich kenn mich mit RS232 nicht so aus, aber auch dort müsste es die Möglichkeit geben, mittels WaitForSingleObjekt() und einer entsprechenden WinAPI-Funktion darauf zu warten, dass Daten zur Abholung bereit liegen.

    Polling, so wie du es in dem Hauptthread und den Threads machst, ist ganz schlechter Stil. Und im Zusammenhang mit Threads bringt es nichts als Probleme.



  • Das heißt, du synchronisierst da nichts, sondern liest blind Werte, ohne zu wissen, in welchem Zustand sich der Thread befindet? Das kann nicht funktionieren...

    Warum interessiert der Zustand des Threads? Der wird doch mit resume() gestartet und läuft dann bis er suspended wird. Der Thread wird auch mit resume nur gestartet, wenn eine Verbindung da ist (d.h wenn status gleich true is). Ansonsten ist der Wert halt 0.

    Was soll denn das Sleep() in dem Thread?

    Das Sleep ist da, damit er sich über den Thread nur alle 1 Sekunde die Werte holt und in die Klasse Messwerte schreibt.

    dort müsste es die Möglichkeit geben, mittels WaitForSingleObjekt() und einer entsprechenden WinAPI-Funktion darauf zu warten, dass Daten zur Abholung bereit liegen

    . Die Daten können ja jederzeit über RS232 abgeholt werden. Da muss doch nicht gewartet werden.



  • Der Zustand des Threads interessiert, weil du nicht wissen kannst, ob der Thread gerade im Sleep steckt, oder gerade den Wert in die Klassenvariable schreibt, oder, oder, oder... Glaub mir, ohne eine Synchronisierung wird das niemals sauber funktionieren. Das ist wie russisches Roulette. Das geht auch nur eine gewisse Weile gut, wenn überhaupt.

    Statt des Sleeps solltest Du dann lieber einen Timer im Thread verwenden.

    Und offensichtlich muß da doch auf den Port gewartet werden. Sonst wäre doch da wohl kaum ein weiteres Sleep vor dem Auslesen der Schnittstelle.

    rudpower schrieb:

    void __fastcall TForm1::timMesswerteAnzeigenTimer(TObject *Sender)
    {
       labAnzeigeTemperatur->Caption = messwerte.GetTemperatur();
       labAnzeigeDruck->Caption = messwerte.GetDruck();
       //usw...
    }
    

    Alleine das kann schon für die Probleme verantwortlich sein. Du greifst ohne Synchronisierung auf die Threads zu.



  • ok, das seh ich ein. Bisher hab ich immer so mit Threads gearbeitet und es ging auch immer gut.
    Den Thread setze ich ja eigentlich ein um alle 1 Sekunde die Daten vom Messgerät zu holen. Deshalb die Zeile (Sleep(1000);).

    Nun möchte ich es richtig machen. Brauche ich überhaupt Threads oder kann ich einfach einen Timer nehmen, der die Daten holt?

    Sollte ich für die Messwerte vielleicht einfach ein struct nehmen und in eine eigene unit packen? Oder vielleicht als private in der frmMain deklarieren?

    Die Daten können erst vom Messgerät empfangen werden, wenn der Benutzer die verbindungsdaten einstellt und einen Button Verbinden drückt. Dies geschieht je Messgerät in einer eigenen Form. Schreibe ich am besten einen Thread für alle Messgeräte oder für jedes Messgerät einen eigenen Thread?



  • Generell ist Synchronisierung natürlich richtig. In diesem speziellen Fall ist aber nicht so schlimm, was der OP da gemacht hat. Er schreibt ja nur Werte in eine Variable, was in einem CPU Zyklus erledigt ist. D.h. der GUI Thread liest entweder noch den alten oder den neuen Wert. Und selbst wenn (es könnte Probleme bei den Double Werten geben), dann würde ja nur ein falscher Wert gelesen mit dem nichts anderes gemacht wird als ihn anzuzeigen - für 1 Sek wäre dann halt in seienm Fenster kein vernünftiger Wert angezeigt.

    Ich vermute eher, dass sich der VCL Thread irgendwodran blockiert.



  • ich bekomm ja auch keinen Fehler sondern das Programm startet einfach nicht. Ich denke mittlerweile, dass es an den vielen USB liegt, die über einen USB Verteiler am Rechner angeschlossen sind. Zieh ich einzelne USB raus funktioniert es tadellos. Es ist aber auch nicht bei bestimmten USB-geräten.
    Ich vermute, dass es Konflikte gibt mit dem USB-Hostcontrollern. ich glaub der rechner besitzt 4 Stück davon.
    Bei Festplatten ist es ja auch so. Wenn man von einer zur anderen Festplatte kopieren will, die an einem Hostcontroller hängen, scheitert dies.



  • Dann wirf doch mal den Debugger an und schau nach, wo das Prgramm hängt...

    Und wer glaubt, dass man nicht synchrinisieren muss, wenn man doch nur ein paar Daten aus dem Thread liest - ja der hat leider schon verloren. Ich kann nur noch einmal dringend davon abraten, ohne Zugriffsschutz auf Threads zuzugreifen.

    Ich kann auch nur einen Blick in die WinAPI empfehlen. Da gibt es eine ganze Reihe nützlicher Events zur Threadsteuerung. Und die CriticalSection aus der WinAPI hat gegenüber der TCriticalSection den Vorteil, dass es ein TryEnter() gibt.

    Ich würde ein Event definieren, dass das Ende des Threads aus dem MainThread heraus sinalisiert. Auf dieses Event wartest du in der Execute des Threads mittels WaitForSingleObject(). Den Timeout auf 1000 ms gesetzt - und schon brauchst du nicht mal mehr einen Timer im Thread. Immer wenn du in den Timeout läufst, liest du die Daten vom Gerät...
    Und wenn es eine Möglichkeit gibt, mit einen Event zu signalisieren, dass die Daten vom Gerät geschrieben wurden (und das sollte es eigentlich geben), dann tausche WaitForSingleObject() gegen WaitForMultipleObject() und du bist auch das zweite Sleep los.

    Wenn ich mir die Threadklasse so ansehe, wage ich schon fast zu bezweifeln, dass der Destruktor des Threads überhaupt noch aufgerufen wird... Und die Tatsache, dass das Programm nach mehreren Versuchen dann doch mal startet, deutet - meiner Meinung nach - sehr stark auf ein Problem mit den Threads hin.

    Multithreading ist allerdings zu komplex um das hier in ein paar Posts zu erläutern, da muss man sich einarbeiten. Wenn du das nicht möchtest, verzichte lieber auf Threads.

    @Morle: Ich weiß nicht, in welchem Bereich der Messtechnik rudpower arbeitet, aber bei uns kostet ein einzelner falscher Messwert Geld. Denn das bedeutet, dass der Sensor nicht einwandfrei arbeitet und verschrottet oder zumindest nachbearbeitet werden muss.



  • kann ich in der Execute des jeweiligen Threads nicht einfach die Daten auf der Hauptoberfläche anzeigen lassen?

    void __fastcall Emko::Execute()
    {
      while(!Terminated) {
        ErfasseWerte();
        frmMain->messwerte.temperatur = temperatur;
        Synchronize(UpdateCaption);
      }
    }
    
    void __fastcall Emko::UpdateCaption()
    {
       frmMain->txtAnzeigeTemperatur->Text = FloatToStr(temperatur);
    }
    
    void Emko::ErfasseWerte()
    {
      if (status) {
        unsigned char send[] = {0x01, 0x04, 0, 0, 0, 0x18, 0xF0, 0};
        unsigned char rec[60];
        rs232.SendData(send, 8);
        Sleep(250);
        rs232.ReceiveData(rec, 60);
        try {
          temperatur  = (rec[3] * 256 + rec[4]) / 10.0;
        }
        catch(...) {
          temperatur = 0.0f;
        }
      }
    }
    


  • Fast... Nur die Zeile

    frmMain->messwerte.temperatur = temperatur;
    

    muss noch mit in die synchronisierte UpdateCaption().
    Du darfst ohne Zugriffsschutz auf nichts außerhalb des Threads zugreifen.


Anmelden zum Antworten