Wann Brauche ich Synchronize

Für Fragen von Einsteigern und Programmieranfängern...
Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Wann Brauche ich Synchronize

Beitrag von Warf »

Also erstmal, finde ich es äußerst interessant das du einen Timer in einem Thread benutzt. Darf man fragen was genau du damit machst? Das klingt für mich nämlich ein bisschen seltsam, denn ein Timer führt ein Event in regelmäßigen Zeitabständen auf dem Main thread aus (da die Timer events über die GUI event queue laufen). Als solches macht es natürlich überhaupt keinen Sinn einen Timer in einem Thread zu erstellen und zu löschen, wenn er so oder so auf dem main thread operiert.

Außerdem kann ich mich theo nur anschließen, nur weils nicht kracht heist es nicht das es nicht korrekt ist. So genannte Race conditions (wenn mehrere Prozesse auf die selbe resource zugreifen wollen ohne geregelte zugrifsskontrolle) sind inherent stochastisch, es kann sein das solche fehler nur alle paar stunden einmal auftreten. Ich selbst hatte mal das problem bei einem größeren Projekt was viele threads verwendet hat das ich sporadisch alle paar stunden mal eine Segfault bekommen hab, was mir beim regulären Testen des Entwicklens nicht aufgefallen ist, erst bei der echten Nutzung des Programs. Ist natürlich trozdem ne schlechte User Experience wenn deine Arbeit der letzten Stunde (war ein editor) plötzlich weg ist wegen einer Zufälligen segfault.

Außerdem nur weils nicht kracht heist es auch nicht das nicht andere Fehler auftreten. Segfaults sind die besten Fehler die dir passieren können, weils dann wenigstens ordentlich kracht. Dein programm kann allerdings auch ohne offensichtliche Fehler in einen Fehlerhaften status gelangen und dann für den rest der Laufzeit einfach falsch weiter laufen.
Ein berühmtes Problem dem sich viele Java Entwickler schon stellen durften ist z.B. mit UTF-16 (was Java bis vor ein paar Jahren noch verwendet hat), wenn durch eine race conditions ein 16 bit char nur halb geschrieben wird, und daraufhin sind plötzlich alle charactere um eins versetzt sind und wenn man dann später in die output (z.b. log) files schaut sind diese komplett unbrauchbar geworden.

Threading ist sehr schwer. Ich habe vor ein paar jahren in der Forschung zu Thread Safety analyse gearbeitet und kann sagen, es richtig zu machen ist wohl eins der schwersten (praktischen) probleme für Programmierer. Wenn du nicht sicher bist das das was du machst korrekt ist, geh am besten davon aus das es kaputt ist und dir irgendwann, um die Ohren fliegt. Und wenn es das tut, vermutlich im ungünstigsten Zeitpunkt der Möglich wäre

Lomat
Beiträge: 28
Registriert: Fr 14. Jan 2022, 13:44

Re: Wann Brauche ich Synchronize

Beitrag von Lomat »

Warf hat geschrieben:
Di 14. Feb 2023, 18:58
Also erstmal, finde ich es äußerst interessant das du einen Timer in einem Thread benutzt. Darf man fragen was genau du damit machst? Das klingt für mich nämlich ein bisschen seltsam, denn ein Timer führt ein Event in regelmäßigen Zeitabständen auf dem Main thread aus (da die Timer events über die GUI event queue laufen). Als solches macht es natürlich überhaupt keinen Sinn einen Timer in einem Thread zu erstellen und zu löschen, wenn er so oder so auf dem main thread operiert.
Im nachhinein gesehen Unsinn. :oops:

Ich habe jetzt in einen Kraftakt das ganze umgebaut. Kein Timer, kein Fenster, nur noch die RS232-Komponente. Im Wesentlichen sorgt jetzt die Execute-Procedure dafür, dass ich den Hauptthread nicht mit einen minutenlangen Sleep blockiere. Das hätte man auch ohne einen weiteren Thread nur mit Timern haben können, aber das hatte ich ja schon und ergab dann andere Komplikationen.

Viele Grüße

Christoph

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6199
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Wann Brauche ich Synchronize

Beitrag von af0815 »

Lomat hat geschrieben:
Mi 15. Feb 2023, 00:08
Kein Timer, kein Fenster, nur noch die RS232-Komponente. Im Wesentlichen sorgt jetzt die Execute-Procedure dafür, dass ich den Hauptthread nicht mit einen minutenlangen Sleep blockiere.
Ok, ich verstehe es nur nicht ganz, weil die RS232 Komponente selbst einen Thread betreibt um nicht zu blockieren. Weil im Hintergrund kapselt die damit den Synaser. Ich vermute du baust das nochmals drüber nach, weil die Komponente irgendwie im Blocking betrieben wird. Was ich nicht ganz kapiere, wieso hast du ein Minutenlanges sleep gebraucht ?! Die RS232 arbeitet über Events, also wenn was da ist bekommst du normalerweise eine Information darüber und den Event kann man verwenden. Da brauche ich keinen sleep. Schau mal in die Komponente hinein, dort wirst du einen Thread werkeln sehen, der die Schnittstelle abfragt ob Daten da sind (und Fehlerbedingungen) und wenn, ja diese abholt und Callbacks auslöst. Das ist genau das, was man braucht um die Schnittstelle im Hintergrund arbeiten zu lassen.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Lomat
Beiträge: 28
Registriert: Fr 14. Jan 2022, 13:44

Re: Wann Brauche ich Synchronize

Beitrag von Lomat »

Hallo nochmal,

im Prinzip ist das Konzept so:

1. Über RS232 den Start der Messung befehlen.
2. Die Zeit für die Messung abwarten. Die wenige Sekunden, aber auch Minuten oder gar Studen daueren. Hier geht es darum, den Hauptthread die Fortschrittinfo anzugeben. Daher folgen mehrere Delays über eine Schleife. Es wird auch überwacht, ob der Benutzer keine Lust mehr hat. Daher kann der Haupthread jederzeit eine Boolean-Varialble auf Abbruch setzen.
3. Ist die Messzeit mit etwas Puffer abgelaufen, dann wird der Befehl zum Ausgeben der Daten über die RS232 gegeben. Die kommen dann in der Ereignisbehandlungsmethode der RS232-Komponente an. Der Hauptthread kann abfragen, ob Daten da sind.

Die Execute-Methode des Threads sieht so aus:

Code: Alles auswählen

Procedure TCommunicationThread.Execute;

Var i : Integer;

Begin
 While Not(terminated) Do
       Begin
         If DataPortSerial.Active Then
           Begin
             iProgress := 0;
             DataPortSerial.Push(Param.stStartInternalMeasurment);
             stLastCommand  := Param.stStartInternalMeasurment;
             iCounter := 0;
             bCancelMeasurement := False;

             While (iProgress <= 105) And Not(bCancelMeasurement) Do
                   Begin
                     Inc(iCounter);
                     iProgress := Round(iCounter * iExtraShortDelay / (iExposuretime * iRepetitionCount) * 100);
                     Sleep(iExtraShortDelay);
                   end;

             If Not(bCancelMeasurement) Then
                Begin
                  stLastCommand := Param.stGetSpectrum;
                  DataPortSerial.Push(Param.stGetSpectrum);
                end;
           end
           Else stLastCommand := '';
         Suspend;
       end;
end;

Bis jetzt verursacht der Code keine Probleme. Ursprünglich habe ich diese Funktionalität über einen Timer realisiert (ohne separaten Thread), aber gemerkt, das bei mehreren Messungen hintereinander die ein oder andere verschluckt worden ist. Von meinen C++-Kollegen wurde mir dann gesagt, dass er die Funktionalität über Multithreading abbilden würde. Dann habe ich mal einfach nach dem Motto Probieren geht über Studieren den Code in einen eigenen Thread ausgelagert und schrittweise umstruktueriert, da ich dank Internetrecherce und diesem Forum gemerkt habe, dass es so nicht geht bzw. eigentlch gar kein Multithreading stattfindet. Im Endeffekt habe ich eine einfache Timer-Funktionalität in den Thread zurückbehalten. Vielleicht würde es auch ganz ohne einen zusätzlichen Thread gehen, aber irgenwie treten keine Fehler mehr auf.

Viele Grüße

Christoph

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6199
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Wann Brauche ich Synchronize

Beitrag von af0815 »

Danke für die Erklärung. Ja das hätte ohne Thread auch funktioniert, wie du schon festgestellt hast. Ich würde eine StateMachine dafür nehmen und die durch einen Timer takten. Das Problem was du gehabt hast, ist entstanden,weil man aufpassen muss bei der Ereignisbehandlung, das man sich gut verriegeln muss, wenn man etwas serialisieren muss und nichts durcheinander kommen soll. Deswegen setze ich da einfache StateMachines ein. Damit ist immer nur ein Zustand gerade gültig.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

TSchnuckenbock
Beiträge: 71
Registriert: Do 20. Jul 2017, 23:47
OS, Lazarus, FPC: Win7 und Win10
CPU-Target: xxBit
Wohnort: Südheide (Schnuckenland)

Re: Wann Brauche ich Synchronize

Beitrag von TSchnuckenbock »

Ich hatte mal ein Projekt, bei dem die Kommunikation auch über die RS232 lief (Gegenstück war ein Faulhaber Motion Controller). Das klappte nach einigen Anläufen ohne separaten Thread sondern nur mit einem Timer (so wie ich mich erinnere). Leider habe ich das Projekt gerade nicht verfügbar, um nachzuschauen.
Nach Erinnerung lief das so, daß die serielle Komponente einen Event hatte, immer wenn ein neuer String rein kam. Dieser String wurde an einen anderen String immer dran gehängt und ein Timer guckte anhand der Trennzeichen (#13#10?) immer mal, ob es was aufzusplitten gab.
Wenn ja, dann wurden die Ergebnisse (Pos-Info, Quittierungen etc) in eine Liste hinten drangehängt zum späteren Interpretieren. Dann könnte es noch einen zweiten Timer gegeben haben, der diese Liste von vorne angefangen von Zeit zu Zeit abarbeitete. Also ein Queue.
Das Projekt lief so zuverlässig, daß man die Steuerung auch 12 Stunden ohne Zicken hin- und herfahren lassen konnte.
Man konnte sogar die RS232 mal für eine gewisse Zeit abziehen und wieder anstecken und das System kam wieder in den richtigen Tritt.

Antworten