Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Thread im Oncreate der Form falsch?
ALF - Fr 30.07.10 20:06
Titel: Thread im Oncreate der Form falsch?
Hi, dazu ein paar kurz Infos.
Die Formen werden zur Laufzeit als MDICHILD erstellt.
Der Thread als solches funktioniert innerhalb der Form.
Mein Problem ist, ich benötige die Daten aber schon wenn die Form
erstellt wird!
Also meine Idee, im Oncreate-ereignis der Form. Mh.... und genau da will, oder kann es nicht funktionieren.
Die Daten werden vom Thread richtig gelesen, wenn es aber zum Abschluss der Procedure Synchronize kommt, kracht es mit EAccessViolation, schreiben von Adresse usw., obwohl die Daten in der Newform übertragen wurden.
Das ist die Procedure die Synchronize aufruft:
Delphi-Quelltext
1: 2: 3: 4:
| procedure TScanThread.UpdatePeaksTh; begin FNform.UpdatePeaks(FpeaksL, FpeaksR); end; |
als wenn die Procedure nicht mehr zurückfindet. Kann das sein??
Der Aufruf aus dem Thread erfolgt so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| procedure TScanThread.Execute; var ..... ..... begin OutputDebugString('ich arbeite'); .... .... while (buflen > 0) and (not terminated) do begin ..... ..... end; OutputDebugString('Daten können abgeholt werden'); Synchronize(UpdatePeaksTh); .... .... OutputDebugString('ich beende mich'); Terminate; end; |
Beim kontollieren mit dem EreignisProtokoll kommt es bis 'Daten können abgeholt werden'
Eine Rückkehr der Procedure gibt es nicht!
Jemand ne Idee ob es so ist, wie ich vermute!
Wenn ja, was muss ich den da noch beachten?
Oder Liegt der Fehler ganz wo anders?
Gruss Alf
jaenicke - Fr 30.07.10 20:13
ALF hat folgendes geschrieben : |
| Mein Problem ist, ich benötige die Daten aber schon wenn die Form erstellt wird! |
Wie stellst du dir das vor? Das dauert schließlich kurz bis der Thread fertig ist. Und da ist dein OnCreate schon vorbei, du wirst die Daten also nie beim Erstellen selbst schon haben.
Wie erstellst und startest du denn den Thread? Kann es sein, dass du FreeOnTerminate benutzt? Wenn du da nicht aufpasst...
Und wo weist du die Formularvariable dem Thread zu? Im Konstruktor oder erst danach? (Wenn danach, musst du natürlich pausiert starten. :idea:)
Ja, und dann ist noch die übliche Frage nach dem Ergebnis des Debuggers? Ist die Formvariable in der Zeile erreichbar?
Und natürlich: An welcher Adresse kommt denn die Zugriffsverletzung? Wenn die sehr klein ist, hast du nen Nullpointer, dann vermutlich die Formularvariable.
ALF - Fr 30.07.10 20:56
jaenicke hat folgendes geschrieben : |
| Wie stellst du dir das vor? Das dauert schließlich kurz bis der Thread fertig ist. Und da ist dein OnCreate schon vorbei, du wirst die Daten also nie beim Erstellen selbst schon haben. |
Beim Debuggen gebrüft, sind die Daten(es sind Arrays in der Form) schon gefüllt!
Also auch da, oder?
jaenicke hat folgendes geschrieben : |
Wie erstellst und startest du denn den Thread? Kann es sein, dass du FreeOnTerminate benutzt? Wenn du da nicht aufpasst...
Und wo weist du die Formularvariable dem Thread zu? Im Konstruktor oder erst danach? (Wenn danach, musst du natürlich pausiert starten. :idea:) |
mh... MainForm=MDIFORM
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| if OpenDialog1.Execute then begin .... .... NewForm:= TForm3.Create(self); end; |
TForm3:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| procedure TForm3.FormCreate(Sender: TObject); begin .... .... TScanThread.Create(mychan1, xzoom, self); end; ..... ..... procedure TForm3.UpdatePeaks(pL, pR: array of smallint); var i: integer; begin for i:= low(pL) to high(pL) do begin wavebufL[i]:= pL[i]; wavebufR[i]:= pR[i]; end;
timer1.Enabled:= true; end; |
Constructor des Threads
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| constructor TScanThread.Create(decoder: HSTREAM; zoom: integer; Nform: TForm3); begin inherited create(false); Priority:= tpNormal; FreeOnTerminate:= true; FDecoder:= decoder; FZoom:= zoom; FNform:= NForm; end; |
jaenicke hat folgendes geschrieben : |
Ja, und dann ist noch die übliche Frage nach dem Ergebnis des Debuggers? Ist die Formvariable in der Zeile erreichbar?
Und natürlich: An welcher Adresse kommt denn die Zugriffsverletzung? Wenn die sehr klein ist, hast du nen Nullpointer, dann vermutlich die Formularvariable. |
Ja die Formvariable ist erreichbar.Die Arrays von TFORM3 werden ja gefüllt mit den Daten vom Thread. es Kracht ja auch beim 'end;' nicht bei 'FNform.UpdatePeaks(FpeaksL, FpeaksR)';
Also nach beenden von 'procedure TForm3.UpdatePeaks(pL, pR: array of smallint);'
1. Meldung: Z.verletzung bei Adresse 00401c19 in Modul Projekt.exe Schreiben von Adresse 000000001
2.Meldung EInvalidPointer ungültige Zeigeroperation!
Also das alte leiden, nicht einhalten von Grundsätzlichen Dingen! richtig?
Welche Möglichkeit habe ich aber dann, die Daten rechtzeitig zur verfügung zu haben, wenn nicht im Oncreate zu erstellen geht?
Gruss Alf
jaenicke - Sa 31.07.10 08:58
Vom Grundsatz her ist das schon ok. Nur beim Thread nicht ganz.
ALF hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| constructor TScanThread.Create(decoder: HSTREAM; zoom: integer; Nform: TForm3); begin inherited create(false); Priority:= tpNormal; FreeOnTerminate:= true; FDecoder:= decoder; FZoom:= zoom; FNform:= NForm; end; | |
Du sagst, dass der Thread sofort loslaufen soll im inherited, weist die Formularvariable aber erst danach zu. Zudem sagst du, dass er sich sofort zerstören soll, wenn er fertig ist. Was aber, wenn er fertig ist bevor die Initialisierung fertig ist? Crash. ;-)
Deshalb mach es lieber sauber:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| private MyThread: TScanThread; ... MyThread := TScanThread.Create(...); MyThread.MyForm := ...; MyThread.Resume;
MyThread.Terminate; MyThread.WaitFor; MyThread.Free; |
ALF - Sa 31.07.10 10:10
Hi, und guten Morgen!
Jo vom Prinzip verstanden, die Erklärung ok.
Aber!! Ich vergass zu erwähnen das der Thread in einer anderen Unit liegt!
Also fang ich an alles zu modifizieren....., wenn ich es so machen will, soll.
Aber nun:
Egal wie ich es nun ansetzte. Entweder die Units kreuzen sich, was auch nicht geht, oder beim zuweisen von: newthread: TScanThread; wird TScanthread nicht gefunden, oder die Procedure von TForm3 zum abholen der Daten ist nicht bekannt!
Egal was ich nun versuche, es kommt immer eine Stelle die nicht zusammen passen will. Ich hab einfach keine Idee mehr :nixweiss:
Selbst die Tuts, und alles was ich in den Foren gefunden habe, geben mir darauf keine Antwort!
Und alles nur, weil ich die Daten am Anfang schon benötige!
Gruss Alf
jaenicke - Sa 31.07.10 10:17
ALF hat folgendes geschrieben : |
| oder die Procedure von TForm3 zum abholen der Daten ist nicht bekannt! |
Das ist eigentlich auch nicht der richtige Weg. Stell in deinem Thread ein Event zur Verfügung, das dich benachrichtigt, wenn die Daten da sind, und hole die dann vom Formular aus ab. Der Thread sollte sowieso nicht wissen wohin die Daten sollen.
Denn der Thread hat nur den Sinn die Daten zu holen und bereitzustellen,
mehr nicht.
Du kannst auch OnTerminate benutzen, aber mit einem eigenen Event hast du (für die Zukunft) auch die Möglichkeit zwischendrin Rückmeldungen zu geben.
Die Daten kannst du dann im Thread per Property bereitstellen. Da du den Aufruf des Events im Thread synchronisierst, kann der Hauptthread auch in dem Event darauf zugreifen. (Du kannst natürlich dem Event auch Parameter mitgeben, Sender sollte sowieso dabei sein.)
ALF - Sa 31.07.10 17:26
Ok habe nun alles neu gemacht, kann also jetzt unabhängig den Thread jetzt so aufrufen,
Dabei übergebe ich aber keine Form mehr!
Rückgabewert ist auch vorhanden
Delphi-Quelltext
1: 2: 3: 4:
| NewThread:= TscanThread.Create(...)
if NewThread.Binfertig then |
Schön, weiss nun aber immer noch nicht wie es weiter geht! Wo kommt die Abfrage hin?
mit dem Event naja, habe mir einiges angeschaut. Diese sind aber so Projektspeziell, das ein Rückschluss, auf mein Projekt, irgendwie nicht passt!?
Selbst wenn ich mir, nach den Vorbildern, ein Event zusammenschreibe, könnte ich damit erst mal nichts Anfangen, da ich nicht weiss wo setzte ich es ein.
Ich bekomme irgendwie nicht den zusammenhang hin, bezogen auf mein oncreate Problem!
Gruss Alf
jaenicke - Sa 31.07.10 17:53
Also, als Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| public property Array1: ... property Array2: ... end;
private FMyThread; TMyThread;
FMyThread := ... FMyThread.OnTerminate := MyThreadTerminate;
procedure TMyForm.MyThreadTerminate(Sender: TObject); begin FMyThread.Array1 ... end; |
Oder du erstellst dir nen Event:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| TMyUpdatePeaksEvent = procedure(Sender: TObject; pL, pR: array of SmallInt) of object;
...
public OnUpdatePeaks: TMyUpdatePeaksEvent read FOnUpdatePeaks write FOnUpdatePeaks;
...
if Assigned(FOnUpdatePeaks) then FOnUpdatePeaks(Self, MyArray1, MyArray2);
private FMyThread; TMyThread;
FMyThread := ... FMyThread.OnUpdatePeaks := MyThreadUpdatePeaks;
procedure TMyForm.MyThreadUpdatePeaks(Sender: TObject; pL, pR: array of SmallInt); begin pL... end; |
Erstellen kannst du den Thread ruhig in OnCreate des Formulars und freigeben entsprechend später in OnDestroy.
ALF - Sa 31.07.10 18:21
Ja, an so was habe ich mich versucht und bin dann da gescheitert!
In der Unit Thread:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| type TMyUpdatePeaksEvent = procedure(Sender: TObject; pL, pR: array of SmallInt) of object;
type TScanThread = class(TThread) private Fdecoder: HSTREAM; Fzoom: integer; Fsec: integer; FpeaksL: array of smallint; FpeaksR: array of smallint; FBinfertig: Boolean; FOnUpdatePeaks: array of smallint; protected procedure Execute; override; procedure SetBinfertig(const Value: Boolean); public constructor Create(decoder: HSTREAM; zoom: Integer; sec: integer); property Binfertig: Boolean read FBinfertig default False; property OnUpdatePeaks: TMyUpdatePeaksEvent read FOnUpdatePeaks write FOnUpdatePeaks; end; |
jetzt bekomme ich, auch vorhin wo ich es mit meinem eigenem event TDatenholen versucht habe, ne Fehlermeldung:
[Fehler] Unit2.pas(29): Inkompatible Typen: 'dynamic array' und 'TMyUpdatePeaksEvent' oder ebend TDatenholen. Hatte ich es also wieder verworfen!
Weiss zwar was die FehlerMeldung bedeutet, weiss aber nicht was ich ändern muss!!
Sorry für C&P
Gruss Alf
jaenicke - Sa 31.07.10 18:25
Naja, fast...
Delphi-Quelltext
1:
| FOnUpdatePeaks: TMyUpdatePeaksEvent; |
Schließlich hast du die Property auch mit diesem Typ deklariert...
ALF - Sa 31.07.10 19:08
Ich verzweifle :cry:
EAccessViolation: schreiben von Adresse 000000001
Aber nur in der IDE!
Starte ich das ohne, keine Fehler!?(noch nicht auf ein anderen Rechner gestestet)
Habe zum Test mal die Freigabe der dynamischen Arrays im Thread auskommentiert, oops keine Fehler mehr, auch in der IDE!
Habe ich die zu früh freigegeben? Hier mal Thread.execute.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| procedure TScanThread.Execute; var PeakBuf: array[0..176400] of integer; ..... ..... begin inherited; try
setlength(FpeaksL, 2600); setlength(FpeaksR, 2600); ..... ..... OutputDebugString('Daten können abgeholt werden'); FBinfertig:= True; Synchronize(UpdatePeaksTh); finally
OutputDebugString('ich beende mich'); Terminate; end; |
Gruss Alf
jaenicke - Sa 31.07.10 19:13
In welcher Zeile tritt der Fehler denn auf? Kann es sein, dass du von außen noch auf ein Array im Thread zugreifst?
Wie sind die Arrays denn deklariert? Mich irritiert das := nil etwas. :gruebel:
ALF - Sa 31.07.10 19:21
An welcher Stelle, wie beim Anfang!
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| procedure TScanThread.UpdatePeaksTh;begin if Assigned(FOnUpdatePeaks) then FOnUpdatePeaks(Self, FpeaksL, FpeaksR);
end; |
nach dem abholen der Daten.
Schau mal 2 Posts weiter hoch von mir, da steht wie TScanThread erstellt wird mit den Arrays:
FpeaksL: array of smallint;
FpeaksR: array of smallint;
Gruss Alf
jaenicke - Sa 31.07.10 19:31
Das hatte ich schon so verstanden, aber da dann der Code nicht viel Sinn macht, habe ich nochmal nachgefragt. ;-)
Es reicht schon die Länge auf 0 zu setzen.
Ja, so sehe ich an dem geposteten Code erstmal auch nix außergewöhnliches. FreeOnTerminate und sowas hattest du ja deaktiviert, oder?
ALF - Sa 31.07.10 20:21
Jo, FreeOnTerminate:= false, sonst krachts auch an der Stelle!!
Sobald ich,
setlength(FpeaksL, 0);
setlength(FpeaksR, 0);
wieder drin hab krachts.
Also muss ich was anderes machen für die Freigabe oder?
Nur komischer weise passiert das nur wenn der Thread im oncreate aufgerufen wird!
Im Programm selber (TForm3) brauche ich den Thread ja auch, also nach create newform;
Da kann es so stehen, setlength(FpeaksL, 0) und nichts passiert!??? Selbst FreeOnTerminate:= True, macht da nix.
Fragen über Fragen, ich weiss, aber wenn man als Anfänger mit sowas (wiedersprüchliches) konfrontiert wird :?
Ich bedanke mich für die super Aussdauer mit mir :zustimm:
Hab auf alle Fälle mehr gelernt, als xTage in allen Foren suchen(was ich ja gemacht habe) und nix finden :D
Ich lass die Freigabe erst mal weg, sonst komm ich nie weiter!!
Edit1: Freigabe der Arrays muss gemacht werden, sonst StackOverFlow :mrgreen:(dyn. Arrays)
Edit2:Feststellung!! Solange die Form in eine Initialisierung steckt kann man keinen Thread starten :!: :?: (Kann man schon, aber nicht mit dyn.Arrays)
Gruss Alf
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!