Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Fehler bei aufruf TThread -->AccessViolation
seji - Do 15.10.09 20:20
Titel: Fehler bei aufruf TThread -->AccessViolation
Hallo zusammen,
ich bins schon wieder ^^
Wie oben schon genannt habe ich ein problem mit Threads.
Ich weiß dazu gibts hier ne Menge im Forum aber nix hat so wirklich mein Problem beschrieben.
Also, was ich im dem Thread machen möchte sind Dateien kopieren, desweiteren will ich das im ne Thread machen weil man dann ja auch den kopiervorgang
abbrechen kann. Dazu habe ich eine Hauptklasse mit GUI(Unit1) etc. und eine mit dem Thread (Unit2).
Den Thread ruf ich in der Hauptklasse wie folgt auf:
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: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, filectrl,Unit2, Menus;
type . . .
private copyGUI:copy; public end;
var . . .
begin
. . .
copyGUI.create; copyGUI.Setpathm3u('C:\Users\All Users\Temp\parsed_playlist.txt'); copyGUI.Setpathcopyto(pathcopyto);
copyGUI.Resume;
. . . end;
procedure TForm1.cancelClick(Sender: TObject); begin copyGUI.Terminate;
end; |
In meiner Thread Unit steht dann folgendes:
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: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124:
| unit Unit2;
interface
uses classes, sysUtils, windows;
type copy = CLASS(TThread)
private pathm3u : String; pathcopyto : String;
protected procedure execute; override; public constructor create; virtual; procedure Setpathm3u (ppathm3u: String); virtual; procedure Setpathcopyto (ppathcopyto: String); virtual;
end;
implementation
uses Unit1;
procedure copy.execute;
var title:String; filename:String; readm3u:String; extension:String; InFile : TextFile; Position:Integer;
begin while not Terminated do;
error_notfound:=0; assignFile (InFile, pathm3u); reset (InFile);
Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Exportiere Datein, bitte warten...');
while not eof (InFile) do begin readln (InFile, readm3u); Position := Pos(':', readm3u); if Position <> 0 then begin extension:=ExtractFileExt(readm3u); filename:=pathcopyto+title+extension;
if Form1.CheckBox1.Checked=True then begin if not FileExists(PChar(readm3u)) then begin Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden'); error_notfound:=error_notfound+1; end else begin CopyFile( PChar(readm3u) ,PChar(filename),false); end; end else begin if not FileExists(PChar(readm3u)) then begin Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden'); error_notfound:=error_notfound+1; end else begin CopyFile( PChar(readm3u) ,PChar(filename),true); end; end;
count:=Round(count_temp); Form1.ProgressBar1.Max:=count; Form1.Progressbar1.Position := Form1.Progressbar1.Position + 1;
end else begin title:=readm3u;
end;
end;CloseFile (Infile);
end;
constructor copy.create; begin inherited create(true); freeOnTerminate := true; end;
procedure copy.Setpathm3u (ppathm3u: String); begin pathm3u := ppathm3u; end;
procedure copy.Setpathcopyto (ppathcopyto: String); begin pathcopyto := ppathcopyto; end;
end. |
beim Compelieren komme keinerlei Fehler. Ich hab das ganze nach nem Tutorial gemacht.
Nur leider kommt sobald den Thread aufrufe/erstelle folgender Fehler:
Bei folgender zeile stiegt mein programm aus:
Delphi-Quelltext
1:
| copyGUI.Setpathm3u('C:\Users\All Users\Temp\parsed_playlist.txt'); |
Muss dahher irgendwas im Thread aber ich hab keinen Plan was. Des weiteren wundert mich es das es schon beim create passiert
und nicht erst wenn der Thread überhaupt mal was macht.
Meine vermutung ist das ich was mit syncronize() machen muss weil ich ja auf Ubit1 zugreife. Hab ich dann auch probiert indem ich aus Unit2 alles rausgenommen habe was mit Unit1 zu tun hat. Bekomme ich aber den selben Fehler.
Vielen Dank schonmal für eure Hilfe.
Gruß Seji
PS: Habe mich an folgendes Tutorial gehalten:
http://www.hsg-kl.de/faecher/inf/material/delphi/threads/index.phpModeriert von
Narses: Topic aus Sonstiges (Delphi) verschoben am Do 15.10.2009 um 20:24
jaenicke - Do 15.10.09 20:24
seji hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3: 4:
| copyGUI.create; copyGUI.Setpathm3u('C:\Users\All Users\Temp\parsed_playlist.txt'); copyGUI.Setpathcopyto(pathcopyto); [...] | |
Du rufst die Methode Create des noch nicht existierenden Objekts copyGUI auf, wie soll das gehen? :gruebel:
// EDIT:
Im Tutorial steht es ja auch wie nicht anders zu erwarten richtig, aber wenn du nicht abschreiben kannst... ;-)
seji - Do 15.10.09 21:10
Oh stimmt das ist ja mega peinlich ....
Da hab ich wohl den Baum vor lauter bäumen net gesehen.
Also dafür schonmal vielen Dank!!
Hätte da aber noch ne frage wegen Terminate. Ich hab nochmal im tut nachgeschaut, so wie ich das sehe hab ich da aber keinen fehler gemacht.
Also mein programm kopiert fleißig aber wenn ich auf cancel drücke passiert leider nix. Da ich meine Thread Unit noch etwas verändert habe, poste ich sie hier nochmal:
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: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127:
| unit Unit2;
interface
uses classes, sysUtils, windows;
type copy = CLASS(TThread)
private pathm3u : String; pathcopyto : String;
protected procedure execute; override; public constructor create; virtual; procedure Setpathm3u (ppathm3u: String); virtual; procedure Setpathcopyto (ppathcopyto: String); virtual;
end;
implementation
uses Unit1;
procedure copy.execute;
var title:String; filename:String; readm3u:String; extension:String; InFile : TextFile; Position:Integer;
begin while not Terminated do begin
error_notfound:=0; assignFile (InFile, pathm3u); reset (InFile);
Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Exportiere Datein, bitte warten...');
while not eof (InFile) do begin readln (InFile, readm3u); Position := Pos(':', readm3u); if Position <> 0 then begin extension:=ExtractFileExt(readm3u); filename:=pathcopyto+title+extension;
if Form1.CheckBox1.Checked=True then begin if not FileExists(PChar(readm3u)) then begin Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden'); error_notfound:=error_notfound+1; end else begin CopyFile( PChar(readm3u) ,PChar(filename),false); end; end else begin if not FileExists(PChar(readm3u)) then begin Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden'); error_notfound:=error_notfound+1; end else begin CopyFile( PChar(readm3u) ,PChar(filename),true); end; end;
count:=Round(count_temp); Form1.ProgressBar1.Max:=count; Form1.Progressbar1.Position := Form1.Progressbar1.Position + 1;
end else begin title:=readm3u;
end;
end;CloseFile (Infile); Self.Terminate; end; Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Fertig'); end;
constructor copy.create; begin inherited create(true); freeOnTerminate := true; end;
procedure copy.Setpathm3u (ppathm3u: String); begin pathm3u := ppathm3u; end;
procedure copy.Setpathcopyto (ppathcopyto: String); begin pathcopyto := ppathcopyto; end;
end. |
Könnte ja sein das ich nen neuen Fehler eingebaut hab ;-)
Gruß Seji
dummzeuch - Do 15.10.09 22:04
seji hat folgendes geschrieben : |
Oh stimmt das ist ja mega peinlich ....
|
Das kann selbst alten Hasen noch passieren...
Aber:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Exportiere Datein, bitte warten...'); if Form1.CheckBox1.Checked=True then begin Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden'); Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden'); Form1.ProgressBar1.Max:=count; Form1.Progressbar1.Position := Form1.Progressbar1.Position + 1; Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Fertig'); |
Diese Zeilen duerfen in einer Execute-Methode eines Threads nicht stehen. Jeglicher Zugriff auf die VCL ist verboten, er darf nur im Haupt-Thread stattfinden, z.B. via Synchronize.
twm
Lossy eX - Fr 16.10.09 09:35
Terminate: Na ja. Die Methode
Terminate setzt einen Wert der innerhalb des Threads (
Execute) abgefragt werden kann und sollte. Also die Eigenschaft
Terminated. Du musst diese Eigenschaft natürlich überall dort überprüfen wo du gerne abbrechen möchtest. Um den Thread zu beenden musst du nur die Methode Execute verlassen. Aktuell ist es in deinem Code so, dass du am Anfang überprüfst ob der Thread beendet wurde und ansonsten deine Liste mit Dateien kopierst. Wenn du sie kopiert hast, dann sorgst du mittels
Terminate dafür, dass sich dein Thread beenden. Dadurch kennt dein Thread nur zwei Stellen an der er aussteigen kann. Bevor er irgendwas gemacht und nachdem er alles erlegt hast. Wenn du das Kopieren nur ein Mal ausführst geht das auch deutlich einfacher.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| procedure copy.execute; begin try try while not (terminated or eof (InFile)) do begin end; finally end; except end; end; |
Problem an dem Code ist aktuell, dass du nicht direkt weißt ob der Thread jetzt beendet wurde oder ob er die Liste komplett abgearbeitet hat. Alternativ zu solche einer Schleife kannst du auch folgendes machen. Je nachdem wie du das gerne haben willst.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| while not eof (InFile) do begin if Terminated then begin exit; end;
end; |
Was mir gerade noch aufgefallen ist. Damit kannst du später ganz böse auf die Nase fallen. Du setzt
FreeOnTerminate innerhalb des Konstruktors deines Thread und benutzt außerhalb des Threads noch die Instanz des Threads. Dabei muss man sehr sehr genau aufpassen. Wenn die Methode Execute fertig ist wird der Thread automatisch frei gegeben. Wenn du dann noch mal auf die Instanz zugreifst (Anzahl/Liste der nicht kopierten Dateien oder so), dann gibts eine Zugriffsverletzung. Also entweder FreeOnTerminate setzen und den Thread komplett alleine laufen lassen oder FreeOnTerminate nicht setzen und dann den Thread selber per Hand freigeben. Dafür kannst du bei der letzten Methode aber sicher sein, dass deine Instanz dir nicht irgendwann unter den Fingern weggezogen wird.
Und ich kann dummzeuch nur zustimmen. Aus einem Thread solltest du nicht auf ein Fenster oder irgendwelchen Fensterelemente zugreifen. Die VCL ist nicht sicher und du kannst dir schon gut Probleme dabei einfangen. Allerdings hast du sehr sehr viele Statusinformationen. Wenn die alle über Synchronize ausgegeben werden solltest du etwas bedenken. Um so mehr Statusinformationen du ausgeben musst um so häufiger muss der Thread warten. Wenns zu viele werden, dann ist der Thread irgendwann nur noch mit Warten beschäftigt. Damit wird der nutzen von Threads geschmälert bis er gegen null tendieren kann. Das aber nur mal so am Rande. Das wird wahrscheinlich später noch mal interessant werden können.
seji - Fr 16.10.09 13:23
Zitat: |
Diese Zeilen duerfen in einer Execute-Methode eines Threads nicht stehen. Jeglicher Zugriff auf die VCL ist verboten, er darf nur im Haupt-Thread stattfinden, z.B. via Synchronize. |
Ich hba das nun mal ausgelagert mit Synchronize(). Und siehe da schon lässt sich der Thread wunderbar beenden.
@Lossy eX: Vielen Dank für deine ausführliche erklärung. Ich hab dann auch deinen zweiten Vorschlag umgesetzt, funktioniert wunderbar.
Also danke euch zwei, habt mir sehr geholfen. Schönes Wochenende.
Gruß Seji
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 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!