Thread-Variablen



  • Hallo Gemeinschaft,

    ich habe gerade in einem Thema (Titel: dynamische Label, Seite 6):

    Ein direktes Beschreiben von Variablen in anderen Threads führt zu undefiniertem Verhalten.

    Da ich gerade ein solches Problem habe (hatte), dazu mal ein paar Fragen:
    (1) Wenn ein Thread Suspended ist, kann ich doch Thread-relevante Variablen vom Hauptprogramm aus problemlos setzen, oder?
    Dieses Vorgehen ist jetzt momentan meine Lösung für folgendes Problem:
    Ein Thread wartet auf Zeichen im Eingangspuffer der seriellen Schnittstelle. Sind Zeichen empfangen worden, wertet der Thread sie aus und sollte eine Bitvariable setzen (Flag= true). Das Hauptprogramm steckt derweil in einer while-Schleife, welche nur bei Flag==true verlassen wird. Tritt nach einer bestimmten Zeit kein Flag==true auf, so ist dies ein TimeOut auf welches entsprechend reagiert wird.
    Tatsächlich sind häufig TimeOuts aufgetreten, obwohl die seriell empfangenen Daten korrekt waren und auch korrekt im Puffer meines Progs lagen. Es wurde ganz einfach das Flag nicht gesetzt (mittels Breakpoint überprüft)!
    Nun halte ich den Thread jedes Mal an, wenn das Hauptprogramm die while-Schleife verlässt (Suspend) und starte ihn wieder, wenn ich weitere Zeichen erwarte (Resume) - plötzlich treten keine TimeOuts mehr auf... (2) Ist das mit dem Zitat gemeint?

    (3) kann ich Thread->Suspend auch im Thread aufrufen?



  • Ein direktes Beschreiben von Variablen in anderen Threads führt zu undefiniertem Verhalten.

    Völlig korrekt.

    Kolumbus schrieb:

    (1) Wenn ein Thread Suspended ist, kann ich doch Thread-relevante Variablen vom Hauptprogramm aus problemlos setzen, oder?

    Nein, kannst Du nicht, da der Thread nicht zwangsläufig 'an einer günstigen Stelle' schlafen gelegt wird.

    Kolumbus schrieb:

    Das Hauptprogramm steckt derweil in einer while-Schleife, welche nur bei Flag==true verlassen wird.

    Extrem schlechte Stil. Das verbrät nur unnötig CPU-Ressourcen und ist - wie Du schon selbst festgestellt hast - fehlerträchtig.
    Ich würde das Problem mit Events oder Botschaften lösen. Man sollte grundsätzlich nicht auf Thread-Variablen pollen. Und schon gar nicht, ohne entsprechenden Zugriffsschutz.

    Ich setze in Threads mittlerweile viel Win-API ein, da dies die geringsten Probleme bereitet. Windows-Botschaften, um das UI über Änderungen zu informieren. Events oder Semaphores um andere Threads über Änderungen zu informieren. Auch WaitForSingleObject oder WaitForMultipleObjects ist einen Blick wert, insbesondere wenn Du auf Daten aus der seriellen Schnittstelle wartest. Auf keinen Fall darfst Du, aus anderen Threads heraus, auf irgendwelche Daten zugreifen, die nicht z.B. über eine CTRITICAL_SECTION geschützt sind. Auch nicht, wenn der Thread suspended ist.



  • Joe_M. schrieb:

    Kolumbus schrieb:

    (1) Wenn ein Thread Suspended ist, kann ich doch Thread-relevante Variablen vom Hauptprogramm aus problemlos setzen, oder?

    Nein, kannst Du nicht, da der Thread nicht zwangsläufig 'an einer günstigen Stelle' schlafen gelegt wird.

    Aha, langsam dämmert mir, warum oft so gearbeitet wird: Thread erstellen, Ausführen lassen, Thread zerstören (nicht nur anhalten). Bringe ich das richtig in Verbindung?

    Joe_M. schrieb:

    [...] Auch WaitForSingleObject oder WaitForMultipleObjects ist einen Blick wert, insbesondere wenn Du auf Daten aus der seriellen Schnittstelle wartest.

    Ja, genau das habe ich auch gerade gedacht, als ich vor 3 Minuten in der BCB3-Hilfe unter TThread-Methoden "WaitFor" gefunden habe. Damit werde ich mich jetzt mal beschäftigen und wenn ich Fragen habe, melde ich mich unter diesem Thema nochmal.

    Danke schonmal bis hierhin



  • Hallo

    Kolumbus schrieb:

    Joe_M. schrieb:

    Kolumbus schrieb:

    (1) Wenn ein Thread Suspended ist, kann ich doch Thread-relevante Variablen vom Hauptprogramm aus problemlos setzen, oder?

    Nein, kannst Du nicht, da der Thread nicht zwangsläufig 'an einer günstigen Stelle' schlafen gelegt wird.

    Aha, langsam dämmert mir, warum oft so gearbeitet wird: Thread erstellen, Ausführen lassen, Thread zerstören (nicht nur anhalten). Bringe ich das richtig in Verbindung?

    Nein nicht unbedingt. Suspend ist schon in Ordnung, du weist von außen nur nicht wo genau der Thread angehalten wurde. Suspend ist nicht zur Thread-Synchronisierung da!

    bis bald
    akari



  • akari schrieb:

    Suspend ist schon in Ordnung, du weist von außen nur nicht wo genau der Thread angehalten wurde.

    Naja, und deswegen wird meist nicht mit Suspend und Resume, sondern eher mit Create und Terminate gearbeitet? Weil man da genau weiß: der Thread läuft von vorne, wenn ich mit Create arbeite!?



  • Hallo

    Du must eben trennen zwischen den Situationen wo du wissen must wo genau der Thread ist und wo nicht. Denn du must ja bedenken da bei jedem Terminate/Create ja alles gelöscht wird was der Thread bisher geleistet hat.
    Zum Beispiel must du nicht wissen wo der Thread grad steht wenn du ein Download-Thread programmieren willst : Der User hat die Möglichkeit den Download anzuhalten (neben der Möglichkeit ganz abzubrechen). Hier must du Suspend verwenden, weil sonst der ganze bisherige Download futsch ist.

    bis bald
    akari



  • Ok, ich denke das ist soweit klar, Danke.
    Bevor ich jetzt mit WaitFor... weitermache: Ich hatte die Idee, den Thread an einer definierten Stelle anzuhalten. Und zwar, wenn der Empfang vollständig (Menge) und korrekt (Inhalt) ist. Und zwar hatte ich vor, im Thread-Code einfach

    this->Suspend();
    

    an einer definierten Stelle einzufügen. Das Problem mit dem Zugriff auf die Thread-Variable wäre ja dann erledigt, da ich im Hauptprogramm den Status des Thread abfragen könnte (Thread-Zustand als Flag). Allerdings habe ich damit ja nicht das Problem mit der for-Schleife im Hauptprogramm gelöst, daher werde ich das nicht machen. Ich möchte an dieser Stelle nur wissen, ob das soweit auch funktionieren würde!?! (Der Compiler beschwert sich nicht darüber)

    Und dann noch eine Frage zum Thema Create: Ist es nicht aufwendiger wenn ich den Thread 10000x neu "create", als wenn ich im Hauptprogramm in einer Schleife den Status abfrage?

    Edit: Außerdem muss ich mir dann was Neues für das TimeOut überlegen, weil ja das Hauptprogramm bei WaitFor... stehenbleibt, nicht wahr? In der for-Schleife kann ich ja die verstrichene Zeit überprüfen.



  • Ich würde es folgendermaßen lösen:

    Thread sperrt Datenobjekt.
    Thread liest Daten aus der Schnittstelle.
    Wenn die Daten komplett sind, gibt der Thread das Datenobjekt frei und sendet eine Botschaft an das entsprechend Form.
    Thread wird mittels WaitForSingleObjekt an der Stelle angehalten.

    Form erhält Botschaft.
    Form sperrt Zugriff auf Datenobjekt und liest die Daten aus.
    Form setzt den Event, auf den Thread wartet.
    Thread setzt arbeit fort. 😉

    Das sollte die Ressourcenschonendste Version sein (schau Dir mal an, wieviel CPU-Zeit das pollen des Threadstatus frisst, mal ganz abgesehen vom Problem mit der Unzuverlässigkeit und nein, auch ein Suspend an einer 'unkritischen' Stelle ist unsauber und kann undefiniertes Verhalten erzeugen...).
    Auch hier ist ein Zugriffsschutz unbedingt notwendig, auch wenn es keinen Sinn zu machen scheint!

    😉 Hier ist das Problen, dass, während der Thread auf die Abholung der Daten durch das Form wartet, bereits neue Daten an der Schnittstelle anliegen können. Hier ist es dann sinnvoll bereits einen neuen Thread zu starten, der schon wieder auf neue Daten wartet, noch bevor die alten abgeholt werden. Sprich der alte Thread wird dann nach Abholung der Daten gelöscht, da es ja schon einen neuen gibt, der auf Daten wartet. Auch einer der Gründe, warum man einen Thread nach abarbeiten der Daten eher löscht, als wieder zu verwenden.



  • Ja, das klingt gut und logisch. Nur 2 Probleme:

    (1A) Ich habe noch nie irgendwelche Datenobjekte (gemeint ist sicherlich zB. der Eingangspuffer-Vektor oder auch das Flag?) gesperrt / freigegeben. Die Suchfunktion bringt zu *sperre* keine für mich verwertbaren Ergebnisse!?! Was ist mit "sperren" gemeint? Bisher habe ich maximal Variablen / Vektoren als private oder public deklariert...

    (2A) Ich habe keine Ahnung, wie ich mit WaitForSingleObject umgehen soll, oder selber Nachrichten hin und her sende - das habe ich noch nie gemacht... Die Hilfe ist auch nicht unbedingt ausführlich dazu.
    Also einfach: WaitFor(RxThread); und das Hauptprogramm wartet, bis der Thread die Execute-Methode abgeschlossen hat oder sich beendet (terminate)?

    Edit: Ich kann es drehen und wenden wie ich will - das entscheidene Kriterium scheint für mich das TimeOut zu sein! Wenn ich eine Botschaft versende, darf nur eine bestimmte Zeit vergehen, bis die Antwort da sein muss. Ansonsten tritt ein TimeOut ein. Das ist ja der Grund, warum überhaupt ein Thread verwendet wird. Ohne TimeOut bräuchte ich keinen Thread, da die Menge der zu empfangenden Daten feststeht und die Kommunikation über Acknowledges gesteuert wird - das könnte ja dann im Hauptprogramm laufen.
    Wenn ich jetzt über die vorgeschlagenen Botschaften gehe, steht der Thread und nichts passiert, wenn keine Daten empfangen werden... Dann muss ich also das TimeOut in den Thread einbauen und mit einem Flag, das nach der Benachrichtigung des Hauptprogramms ausgewertet wird, anzeigen, ob es ein TimeOut gab?!?



  • Nun ja, das Datenonbjekt ist eine einfache struct oder class. Zum Thema Sperren, schau mal in der MSDN unter CRITICAL_SECTION. Zum versenden von Botschaften, wirf einen Blick auf PostMessage().

    Wie holst Du denn zur Zeit Daten von dem Port, wenn nicht mittels WaitForSingleObject? Das Problem bei WaitForSingleObject ist, dass es wirklich nur auf eine bestimmte Aktion wartet. Hier wäre eine WaitForMultipleObjects besser, denn kann unendlich lange auf Input vom Port warten und dennoch über einen Event (der in der Anwendung ausgelöst wird) das Beenden des Threads einleiten. Und wieso willst Du in der Hauptwanwendung auf den Thread warten? Wozu? Der Thread soll sich per Nachricht melden, wenn Daten zur Abholung bereit stehen?!?

    Deinen letzten Satz verstehe ich nicht. Welches TimeOut musst Du wo, wann und warum auswerten?!?



  • Ok, CRITICAL_SECTION und PostMessage schaue ich mir an - Danke.

    Langsam dämmert mir, was mit WaitForSingleObject gemeint ist... Das ist nur ein Überbegriff für eine Anzahl an Methoden, wie zum Beispiel WaitCommEvent, habe ich das jetzt gerafft?
    Und WaitCommEvent benutze ich im Thread, um auf Datenempfang am COM-Port zu warten. Das Problem dabei: Was passiert, wenn keine Daten ankommen? WaitCommEvent wartet ja, bis Daten eingegangen sind. Wenn keine Daten eingehen, bleibt der Thread bei WaitCommEvent stehen... Dazu brauche ich das TimeOut im Hauptprogramm.
    Wenn ich innerhalb einer festen Zeit keine Antwort auf beispielsweise eine Anforderung bekomme, muss ich die Anforderung nochmal senden - daher brauche ich ein TimeOut. Ich kann nicht einfach auf gut Glück warten, ob ich Daten am COM-Port erhalte... Ich hoffe das macht das Problem etwas klarer.



  • Kolumbus schrieb:

    Wenn ich innerhalb einer festen Zeit keine Antwort auf beispielsweise eine Anforderung bekomme, muss ich die Anforderung nochmal senden - daher brauche ich ein TimeOut.

    Ja, aber doch nicht innerhalb der Hauptanwendung, sondern innerhalb des Threads. Wie willst Du denn in der Hauptwanwendung wissen, ob ein Timeout stattgefunden hat?

    So langsam verstehe ich allerdings, warum Du auf Threadvariablen zugreifen willst. Mach das nicht. Damit kommst Du nur in Teufels Küche... Der Thread selbst sollte sich um alles relevante kümmern, inkl. des Sendens der Anfrage. Der Thread sollte dann der Hauptanwendung nur mitteilen, ob Daten zur Abholung bereit liegen, oder ob ein Timeout stattgefunden hat. Die Anwendung kann dann entweder die Daten abholen, oder eine neue Anfrage starten lassen.

    So weit ich das verstehe, wartest Du nicht wirklich auf Input vom Port, sondern sendest eine Anfrage über den Port und wartest dann auf eine Rückmeldung über den Port. Unterm Strich wäre es wohl am sinnvollsten, für jede Anfrage einen neuen Thread zu starten, der die gesamte Kommunkiation mit dem Port übernimmt.



  • So,

    mit Hinsicht auf die Tatsache, dass das Ganze Thread-Thema nicht ganz trivial ist und ich nicht wirklich erfahren bin, habe ich nochmal überlegt und einen Dreh gefunden wie ich ohne Thread auskomme und trotzdem ein TimeOut integriert habe. Da die Anwendung den Kommunikationsablauf steuert, steh ich ja nicht unter Druck und weiß wann Daten kommen sollen - das hast du richtig verstanden Joe_M (ja, ich bin nicht sonderlich gut im Erklären, aber Übung macht den Meister).

    Also vielen Dank für eure Anregungen und Tips. Wenn ich nochmal mit Threads zu arbeiten hab, greife ich bestimmt hierauf zurück.


Anmelden zum Antworten