Kurze Frage zu Threads und FreeOnTerminate [gelöst]

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
laz847
Beiträge: 114
Registriert: Mi 18. Jun 2014, 16:39

Kurze Frage zu Threads und FreeOnTerminate [gelöst]

Beitrag von laz847 »

Hallo, bin mir bei etwas unsicher.

Code: Alles auswählen

constructor TMyThread.Create(CreateSuspended : boolean);
  begin
    FreeOnTerminate := True;
    inherited Create(CreateSuspended);
  end;

Müsste es nicht so sein?

Code: Alles auswählen

constructor TMyThread.Create(CreateSuspended : boolean);
  begin
    inherited Create(CreateSuspended);
    FreeOnTerminate := True;
  end;
Zuletzt geändert von laz847 am Di 31. Mär 2015, 00:47, insgesamt 2-mal geändert.

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Kurze Frage zu Threads

Beitrag von Socke »

Im zweiten Beispiel kannst du nicht sicher sein, dass dein Thread bereits fertig ist und damit das FreeOnTerminate gar nicht mehr zum Zuge kommt.

Daher mein Vorschlag:

Code: Alles auswählen

constructor TMyThread.Create(CreateSuspended : boolean);
  begin
    inherited Create(True);
    FreeOnTerminate := True;
    if not CreateSuspended then
      Start;
  end;
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

laz847
Beiträge: 114
Registriert: Mi 18. Jun 2014, 16:39

Re: Kurze Frage zu Threads

Beitrag von laz847 »

Ohh mein Gott, tut mir voll leid aber ich habe diese Antwort übersehen, danke!

Ich bin (wie anderen Themen zu entnehmen ist) auf der Suche nach einer sporadisch auftretenden Access Violation.

Code: Alles auswählen

constructor TEvoThread.Create(const _tid:Byte);
begin
 
 FreeOnTerminate := False;
 
 inherited Create(false); // CreateSuspended false startet sofort
 
...............

Könnte hier die Ursache für meine gesuchte Access Violation liegen oder ist die Reihenfolge so richtig?

Code: Alles auswählen

  for tid := 0 to tcp - 1 do begin
 
   if(arr[tid] = nil) then arr[tid] := TEvoThread.Create(tid + 1);
 
   if(tid = 0) then while(not(arr[0].thread_1_ready)) do Application.ProcessMessages;
 
  end;
 
  for tid := 0 to tcp - 1 do if(Assigned(arr[tid])) and not(arr[tid].Terminated) then Sleep(1);
 
  for tid := 0 to tcp - 1 do if(Assigned(arr[tid])) then FreeAndNil(arr[tid]);

Der erste Thread hat Steuerungsfunktionen und thread_1_ready muss gesetzt sein bevor die anderen starten dürfen.

Paßt das so?

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von Scotty »

laz847 hat geschrieben:Application.ProcessMessages;

Das ist böse in einem Thread.
Ansonsten könnte ich mir vorstellen, dass du auf einen arr[tid] zugreifst, der gerade im Freigeben begriffen ist.
Linux ist bei solchen Fehlern etwas robuster, d.h. vielleicht schneller als Windows. Zumindest war das meine Beobachtung.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von Michl »

Scotty hat geschrieben:Das ist böse in einem Thread.
mMn steht zweite gepostete Code im MainThread, daher unbedenklich. laz847 will nur auf den Start des ersten Threads warten, bevor die nächsten gestartet werden.

@laz847:
Die Zugriffsverletzung könnte auch dadurch entstehen, dass mehere Threads auf eine Variable zugreifen. Verwendest du eine Variable für mehrere Threads? Wenn ja, dann schau dir mal die Nutzung von CriticalSections an.
Greifst du auf Variablen oder Methoden des Mainthreads zu? Da wäre ein Thema Synchronize.
usw.

Müsstest wahrscheinlich mehr vom Code zeigen?!

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

laz847
Beiträge: 114
Registriert: Mi 18. Jun 2014, 16:39

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von laz847 »

Code im MainThread, daher unbedenklich. laz847 will nur auf den Start des ersten Threads warten, bevor die nächsten gestartet werden.

Korrekt!
Verwendest du eine Variable für mehrere Threads? Wenn ja, dann schau dir mal die Nutzung von CriticalSections an.

Ja und CriticalSections hab ich bereits auf dem Schirm, ich würde nur gern DEN FEHLER FINDEN bevor ich ihn evtl. behebe und dann niemals erfahre, was genau ihn ausgelöst hat. Ich habe ein globales globA[][], in diesem hat jeder der 10 Thread's einen Speicherplatz, vereinfacht dargestellt.

Die erste Dimension wird einmalig beim Start vom Mainthread resized, jeder Thread resized und beschreibt nur den ihm zugteilten Platz in der 2 Dimension.

thread 1 >> globA[0][]
thread 2 >> globA[1][] usw...

Im Mainthread LESE ich dieses Array ausschliesslich 1 x nachdem die Threads beendet wurden und bevor diese neu gestartet werden. Das könnte ich in eine CriticalSection packen (habe ich auch vor) allerdings muss ich dann evtl. wieder 1-2 Wochen testen ob es jetzt weg ist. Daran kanns doch aber eigentlich nicht liegen, solange der Mainthread das Array nur liest oder?

Greifst du auf Variablen oder Methoden des Mainthreads zu?

Nur auf dieses Array, sowie irgendetwas anderes aus der Hauptunit als dieses Array verwendet wird, erfolgt der Zugriff nur über Syncronize(@function). Selbst meine simple Logfunktion die nur Text in ein Memo schreibt habe ich syncronisiert. Insgesamt sinds leider 15 syncronisierte Funktionen, die aber nie alle sondern nur je nach Bedarf vom Thread verwendet werden, insgesamt über 10000 Zeilen Code was evtl. Deine andere Frage beantwortet :D.

Das is ja das Schlimme, man baut und baut und am Ende wenn das Projekt fertig ist, läufts, geht alles tagelang wunderbar, bis es nach einigen Tagen zum ersten Mal abstürzte. Mir is ganz übel geworden, seit dem suche ich und finde es nicht (2-3 Monate) :cry:. Da diese blöde Access Violation scheinbar vollkommen zufällig auftritt, habe ich bisher 0 Anhaltspunkte. Die Threads brauchen für Ihre Berechnungen 200 - 500 ms pro Durchlauf, mein System rechnet also 2-3 x pro Minute 24 x 7 und das tagelang ohne Pause, ohne Probleme. Dann auf einmal erzeugt genau dieser Code eine Access Violation, nachdem er schon 1000000 mal das Gleiche ohne Fehler gemacht hat?

Laß ich es über den Debugger laufen bleibt der einfach irgendwann stehen. Hatte ich gerade eben, im Threads-Window von Lazarus sehe ich ja wie die Threads arbeiten, die bleiben einfach stehen ohne eine Meldung. Drück ich dann Pause und wieder Run gehts einfach weiter.

Ohne Debugger stürzt mir unter Qubes irgendwann die VM ab (freeze) in der die Software läuft. Wenn die exe unter Linux Mint (wine) läuft wird eine Access Violation erzeugt, allerdings fehlen mir hier dann jegliche Infos wo, wann, warum. Mein Logfile bleibt leer, keine meine Überprüfungen meldet irgendeinen Fehler oder sie sind nicht mehr in der Lage diesen auszugeben wenn er auftritt.

Ich bin jetzt beim Ausschlußverfahren und versuche alles, jeden kleinen Fitzel so korrekt wie nur möglich zu machen. Selbst da weiß ich jetzt aber nicht wann ich den Fehler behoben habe da ich ihn nicht reproduzieren kann. Quellcode hoch und runter, Ram mit Memtest gechekt, Cpu Last, Temperaturen, Netzteil alles ok...

Nochmal so nebenbei, weil keiner was dagegen gesagt hat, das hier ist also korrekt so?

Code: Alles auswählen

FreeOnTerminate := False
inherited Create(false); // CreateSuspended false startet sofort

Oder besser wie socke es geraten hat?

Code: Alles auswählen

constructor TMyThread.Create();
  begin
    inherited Create(True);
    FreeOnTerminate := True;
    Start;
  end;

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von Michl »

laz847 hat geschrieben:Die erste Dimension wird einmalig beim Start vom Mainthread resized, jeder Thread resized und beschreibt nur den ihm zugteilten Platz in der 2 Dimension.

thread 1 >> globA[0][]
thread 2 >> globA[1][] usw...
Ich weiss zwar nicht, ob das der Fehler ist, auf jeden Fall klingt das mir verdächtig.

Wird die zweite Dimension in der Länge angepasst und passt nun dieses Array nicht mehr in die alte Speicherstelle, wird damit das Array im Speichern woanders hin verschoben. Passiert das zufällig in dem Moment, wo ein Thread darauf schreibend oder lesend zugreift, knallt es.

Greifst du irgendwo auf eine Variable zu (lesend oder schreibend), die von einem anderen Thread (egal ob dies ein Thread oder der Mainthread ist) verändert wird, kommst du um CriticalSections nicht herum. Nur Lesend von allen Threads ist kein Problem.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von theo »

Kleine Klugscheißerei am Rande.
Zwei Dinge eignen sich definitiv nicht für den "Versuch und Irrtum" Ansatz: Unicode und Threads.
Beides sieht nämlich nur schnell mal so aus, als würde es funktionieren. :wink:

laz847
Beiträge: 114
Registriert: Mi 18. Jun 2014, 16:39

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von laz847 »

Mojen ihr Lieben, Ihr seid so cool! :D

wird damit das Array im Speichern woanders hin verschoben.

Ohhhh, jetzt hat was Klick gemacht :D. Es wird von dem einzelnen Thread zwar "nur" die (seine) 2 Dimension resized, beim Vergrössern wird aber das gesamte Array im Speicher verschoben, dass muss man natürlich erstmal wissen, weiß ich ja dank Euch jetzt ;)! Ach jetzt versteh ich das erst, ich hatte nen Denkfehler / Wissenslücke. Ich habe zwar jedem Thread seinem eigenen einzigen Speicherplatz im globalen Array verschafft - in der Hoffnung das sich dadurch Schreib / Lesezugriffe nicht stören.

Aber wenn z.B. Thread 5 seinen Platz resized und der wird grösser wird zwar augenscheinlich ja nur globA[4 (tid-1)][] resized, dazu wird aber das gesamte globA im Speicher an einem anderen Platz neu erstellt. So hat zwar jeder Thread seinen eigenen Speicherbereich aber der liegt dann auf einmal nicht mehr da wo er gerade noch war ??? Und genau das kann passieren während ein anderer Thread noch in den Daten rumwurstelt.

Da genau dieses Vergrössern nur sporadisch (manchmal nach 2 Stunden manchmal 3 Tage gar nicht) passiert, könnte es evtl. die (oder eine der) Ursache(n) sein! Verdammte Axt diese tückischen kleinen Käfer, weiß jetzt auch nicht ob es DER Fehler ist, aber es ist nicht stable. Sorry an alle die versucht haben mir das zu sagen, irgendwie hab ich es vorher falsch / unvollständig verstanden. Mal eine Nacht drüber schlafen neeeeech :D :oops:.

Und wenn man das kapiert hat, versteht man auch warum in diesem Fall eine CriticalSection "helfen" könnte. Hier schreibt zwar keiner der Threads direkt in den Speicherbereich des anderen, aber jeder kann alle verschieben. Allerdings würde ich mir an dieser Stelle damit selbst ins Knie schiessen, mit einer CriticalSection geht dann in meinem Fall nichts mehr parallel.

Hmmm, wie und wohin kopiere ich denn nun am sinnvollsten die Daten wenn diese:

a) Gesammelt dem Main-Thread nach Beenden der Sub-Threads zur Verfügung stehen sollen.

b) Dem Sub-Thread aber beim nächsten Start und während seiner Laufzeit zur Verfügung stehen sollen und der / die SubThread(s) evtl. die Grösse ändern können muss? Während andere SubThreads aber auch gerade mit den Daten (aber nur mit den eigenen) arbeiten.

Kleine Klugscheißerei am Rande.

Wenn man was lernen will sind Klugscheißer immer willkommen, nur dumme Menschen haben damit ein Problem wenn jemand mehr weiß :D! Und Du sprichst damit die Stellen an, die ich eigentlich im Verdacht hatte.

Erklär mal bitte wo Du und welche Probleme Du da siehst, mein Client läuft in MQL4 auf dem Metatrader 4 (der meine Software / Server) mit Daten versorgt und der Client arbeitet mit Unicode. Der Client erstellt Memory Mapped Files und kopiert alle paar Millisekunden in diese Daten, da er mit Unicode arbeitet, verwende ich im Server(Lazarus) OpenFileMappingW:

Code: Alles auswählen

type
TSymbolsArray     = Array[0..255] of TSymbolsStruct;
PSymbolsArray     = ^TSymbolsArray;
 
var
PSymbol : PSymbolsArray  = nil;
 
procedure GetSymbolData(); // verkürzte Ablauf Darstellung ohne Prüfungen usw..., aktualisiert Daten im MainThread bevor die SubThreads starten
begin
 
 if(PSymbol = nil) then begin
 
  MMF_HANDLE_SYM := OpenFileMappingW(FILE_MAP_READ , False , PWideChar(WideString(MMF_NAME_SYM)));
 
  PSymbol := MapViewOfFile(MMF_HANDLE_SYM , FILE_MAP_READ , 0 , 0 , 0);
 
 end;
 
 if(Assigned(PSymbol)) then begin
 
  Symbol := PSymbol^;
 
 end else..............
 
end;
 

Ich hab jetzt mal im Codeteil den ungefähren Ablauf dargestellt, der orginale Quellcode wäre zu umfangreich.

OpenFileMappingW und PSymbol := MapViewOfFile wird quasi nur einmal am Anfang gemacht

Die Schreib / Lese Zugriffe von Client und Server hab ich hier per Mutex / WaitForSingleObject gesichert, Dir macht aber Unicode / Ansi Bedenken oder?

PS: Und woher weißt Du das mit Unicode, vermutlich aus einer anderen Frage weil hier steht davon nichts oder?

DANKE :mrgreen: !!!
Zuletzt geändert von laz847 am So 29. Mär 2015, 16:35, insgesamt 2-mal geändert.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von mse »

laz847 hat geschrieben:Ohhhh, jetzt hat was Klick gemacht :D. Es wird von dem einzelnen Thread zwar "nur" die (seine) 2 Dimension resized, beim Vergrössern wird aber das gesamte Array im Speicher verschoben,

globA ist ein "array[0..n] of array of theType"? Dann ist globA ein array of pointer mit fester Speicherposition. "setlength(globA[n],theSize)" ändert nur den pointer mit Index n.

laz847
Beiträge: 114
Registriert: Mi 18. Jun 2014, 16:39

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von laz847 »

Ansonsten könnte ich mir vorstellen, dass du auf einen arr[tid] zugreifst, der gerade im Freigeben begriffen ist.

Mal noch eine Frage zu dieser Stelle , bin mir da unsicher:

for tid := 0 to tcp - 1 do if(Assigned(arr[tid])) and not(arr[tid].Terminated) then Sleep(1);

for tid := 0 to tcp - 1 do if(Assigned(arr[tid])) then FreeAndNil(arr[tid]);

Wann und wo wird vom Thread Terminated gesetzt, es kann sein das ein Thread mal länger z.B. 50 oder 100 ms braucht.

Besteht hier die Gefahr das der genilt wird während er noch läuft?

Ich hatte es hier schon mal mit WaitForMultipleObjects versucht habs aber irgendwie nicht zum Laufen bekommen?
Zuletzt geändert von laz847 am So 29. Mär 2015, 16:29, insgesamt 2-mal geändert.

laz847
Beiträge: 114
Registriert: Mi 18. Jun 2014, 16:39

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von laz847 »

globA ist ein "array[0..n] of array of theType"? Dann ist globA ein array of pointer mit fester Speicherposition. "setlength(globA[n],theSize)" ändert nur den pointer mit Index n

Nee leider nich :D, ich war so schlau es in beiden Ebenen dynamisch zu machen. Die erste Ebene wird beim Create vom Hauptprogramm auf die erforderliche Anzahl resized :oops:.

"setlength(globA[n],theSize)" ändert nur den pointer mit Index n.

Das wars was ich eigentlich erreichen wollte, da mir zu diesem Zeitpunkt aber nicht klar war, was beim Vergössern im Speicher passiert kam Stuss dabei raus :D.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von mse »

laz847 hat geschrieben:
globA ist ein "array[0..n] of array of theType"? Dann ist globA ein array of pointer mit fester Speicherposition. "setlength(globA[n],theSize)" ändert nur den pointer mit Index n


Nee leider nich :D, ich war so schlau es in beiden Ebenen dynamisch zu machen. Die erste Ebene wird beim Create vom Hauptprogramm auf die erforderliche Anzahl resized :oops:.

Einmalig beim Start? Dann ist die Speicherposition ebenfalls fix. Falls die Grösse der ersten Ebene von globA während des Programmlaufs verändert wird, ist vermutlich eine "threadvar" Variable für die thread-Arrays besser geeignet.

laz847
Beiträge: 114
Registriert: Mi 18. Jun 2014, 16:39

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von laz847 »

Hmmmm Mist, das wars also auch nicht :shock: ? Das hab ich so, denn so hatte ich es mir ja anfangs gedacht.

SetLength(globA , rc , 0);

Und das wird nur einmal beim Start des Programms gemacht.

Falls die Grösse der ersten Ebene von globA während des Programmlaufs verändert wird

Nein dann nicht mehr, es werden bereits am Anfang alle aktuell nötigen Plätze (1 Ebene rc 40, 2 Ebene 0) erstellt.

Von den 40 vorhandenen brauche ich maximal 10, deswegen 10 Threads, nur welche 10 von 40 das sind, entscheidet das Programm.

Jedem Thread wird vorm ersten Start ein Index zugeordnet und dieser ändert sich bis zum Beenden nicht mehr.

Jeder Thread greift nur auf seinen Index zu, keiner liest, schreibt oder resized den Index eines anderen.

Wenn die Threads laufen, wird lediglich die 2 Ebene (durch die Threads) resized, welche beim Start mit 0 initialisiert wurde. Link zum Thema http://www.lazarusforum.de/viewtopic.php?f=10&t=8639

Während die Threads laufen kann die Anzahl nicht verändert werden, bzw. müssen die Threads gestoppt werden und die Software bei Änderungen in diesem Bereich dann erst beendet und neu gestartet werden.

Du sagst also das sich das dynamische wie das statische verhält wenn es nur einmal am Anfang resized wurde?

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Kurze Frage zu Threads und FreeOnTerminate

Beitrag von mse »

laz847 hat geschrieben:Du sagst also das sich das dynamische wie das statische verhält wenn es nur einmal am Anfang resized wurde?

Falls globA als "array of aray of theType" definiert ist ja.

Antworten