![]() |
TList mit Record füllen und an weitere TList übergeben
Entweder war ich gestern Abend zu müde oder das was ich vorhabe funktioniert nicht.
Ich habe folgenden Record:
Delphi-Quellcode:
Ich erzeuge mit New() die Datensätze (Records) füge sie der TList mit Add() hinzu.
type
TDataType = (dtFile, dtDirectory); type PFileEntry = ^TFileEntry; TFileEntry = packed record sFileName: String; aDataType: TDataType; end; var aProcessFileEntry: PFileEntry; aProcessFileList_Record: TList; // Holder An einer späteren Stelle muss ich die Liste aussortieren. Ich erzeuge also eine neue TList und füge gewisse Teile der originalen Liste hinzu:
Delphi-Quellcode:
Mein Problem ist jetzt, dass irgendetwas nicht funktioniert und ich schätze mal es liegt an dieser zweiten Liste.
for j := 0 to iCnt - 1 do
aTmpFileList_Record.Add(aProcessFileList_Record.Items[j]); Die zweite Liste hat die korrekte Länge (Count) an Datensätzen aber beim eigentlichen Prozess, wo die Liste benötigt wird, gehts nicht weiter (keine AV, nichts). Mache ich etwas falsch beim hinzufügen in die aTmpFileList ? |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Klar kann man noch mit New & Dispose arbeiten.. Aber warum nimmst Du keine Klasse und eine ObjectList mit OwnsValue und schon brauchst Du Dich um nix mehr kümmern... Mavarik |
AW: TList mit Record füllen und an weitere TList übergeben
:o
Kannst du mir weiteres dazu erklären? Das kenne ich noch nicht. |
AW: TList mit Record füllen und an weitere TList übergeben
Delphi-Quellcode:
Jetzt kannst Du beliebig viele Instanzen dieser Klasse erzeugen und einer TObjectList hinzufügen. Wenn deren Eigenschaft OwnsObjects true ist (Standard), musst Du die Objekte nicht einmal mehr selbst freigeben, dann kümmert sich die TObjectList selbst darum.
type
TFileEntry = class private FFileName: String; FDataType: TDataType; public property FileName: string read FFileName write FFileName; property DataType: TDataType read FDataType write FDataType; end; |
AW: TList mit Record füllen und an weitere TList übergeben
Wie erzeuge ich denn Instanzen einer Klasse? Dumme Frage :shock:
Bin jetzt so weit:
Delphi-Quellcode:
type
TDataType = (dtFile, dtDirectory); type PFileEntry = ^TFileEntry; TFileEntry = class(TObject) // <= Object sFileName: String; aDataType: TDataType; end; type TMyClass = class(TComponent) public FFileEntry: TObjectList; constructor Create; overload; end; var aProcessFileList_ObjectList: TFileEntry; implementation procedure TMyClass.Create; // hier geht es nicht weiter begin FFileEntry := TObjectList.Create; FFileEntry.OwnsObjects := true; end; |
AW: TList mit Record füllen und an weitere TList übergeben
Mal ein Beispiel im Editor heruntergetippt:
Delphi-Quellcode:
type
TFileEntry = class private FFileName: String; FDataType: TDataType; public property FileName: string read FFileName write FFileName; property DataType: TDataType read FDataType write FDataType; end; ... procedure TDeinForm.ButtonTestClick(Sender: TObject); var Objects: TObjectList; FileEntry: TFileEntry; i: integer; begin Objects := TObjectList.Create; try FileEntry := TFileEntry.Create; FileEntry.Filename := 'C:\Dings\Bums\Trallalla.txt'; FileEntry.DataType := dtFile; Objects.Add(FileEntry); FileEntry := TFileEntry.Create; FileEntry.Filename := 'C:\Dings\Bums'; FileEntry.DataType := dtDirectory; Objects.Add(FileEntry); for i := 0 to Objects.Count - 1 do ShowMessage((Objects[i] as TFileEntry).Filename); finally Objects.Free; end; end; |
AW: TList mit Record füllen und an weitere TList übergeben
Wozu brauchst du unbedingt den Pointer? Man kann auch den Record einfach so in die Liste reinschmeißen (oder alternativ die Klasse wie von DeddyH beschrieben). Das ganze Pointer-Gedöns würde ich weglassen, wenn es nicht auf triftigen Gründen wirklich notwendig sein sollte.
|
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
So hat man das früher eben gemacht... BTW - Schneller ist es sowieso!! Mavarik |
AW: TList mit Record füllen und an weitere TList übergeben
Die nicht-generische TList enthält aber nun einmal Pointer. Wie willst Du da direkt einen Record reinschreiben?
|
AW: TList mit Record füllen und an weitere TList übergeben
Ich bin zwar ein totaler Nichtskönner was Delphi angeht, mit dem Pointer ist aber leider Angewohnheit.
Danke für eure Hilfe! Eine letzte Frage habe ich aber noch. Kann es sein, dass
Delphi-Quellcode:
direkt auc Free igendwo aufruft? Denn nachdem ich
MeineObjectList.Clear;
Delphi-Quellcode:
aufgerufen habe, flattert mit eine AV ins Haus wenn ich auf die ObjectList zugreifen möchte.
.Clear;
|
AW: TList mit Record füllen und an weitere TList übergeben
Clear löscht alle Elemente aus der Liste (und gibt sie ggf. frei), die Liste selbst bleibt aber erhalten.
|
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
|
AW: TList mit Record füllen und an weitere TList übergeben
Mhhhh kann ich eine ObjectList denn nicht leeren ohne das Object zu zerstören?
Hier knallt es:
Delphi-Quellcode:
// for-Schleife von 0 bis 1
if i > 0 then begin aTmpFileList_ObjectList.Clear; // <== da knallt es end; |
AW: TList mit Record füllen und an weitere TList übergeben
OwnsObjects auf false setzen, dann bist Du selbst verantwortlich.
|
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Das wäre doch ein sehr überraschender Effekt. Wenn ein .Delete() oder ein .Clear ein .Free des ListenItems nach sich zieht ok, aber ein .Free auf die Liste?? Gruß K-H P.S. die sieben Zeilen um in ein .Delete ein Dispose für den Pointer zu packen ist ja nun nicht DER Aufwand. |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
|
AW: TList mit Record füllen und an weitere TList übergeben
Scheinbar mag mich die Object-List nicht.
Kurze erklärung: ich habe eine Liste von Dateien. Die speichere ich mit noch ein paar anderen Informationen in diese "Instanzen" von TFileEntry ab. Alle Instanzen kommen dann in ObjectList-A. Nun gehe ich diese ObjectList-A durch und teile sie in n-Stückchen auf. Mit diesen Stücken werden Threads gefüttert und gestartet. Bei 100 Instanzen erhält bei 5 Threads jeder Threads also 20 Instanzen, die er abarbeiten soll. Mein einfacher Test: es gibt 10 Instanzen und 2 Threads. 5 Pro Thread. jetzt das Seltsame: beide Threads bekommen dieselbe Instanz! Meine Logik ist also: - Dateiliste erstellen - Für jede Datei 1 Instanz von TFileEntry erstellen - alle Instanzen in die ObjectList-A Nun ObjectList-A.Count durch die Anzahl der Threads teilen. Ergibt 5 Instanzen pro Thread / 2 Threads. Diese 5 Instanzen werden in eine temporäre ObjectList-B geschrieben, die kurz vorher außerhalb der for-schleife erzeugt wird. Am ende der for-Schleife übergebe ich ObjectList-B an eine Procedur wo nichts weiter drin steht als, dass ein TThread erzeugt werden soll. Er hat ein Property und das ist eine ObjectList. |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Ich könnte mir vorstellen wo dein Fehler ist, aber ich will nicht wild drauf losraten. :stupid: |
AW: TList mit Record füllen und an weitere TList übergeben
Ihr werdet mich für den Code HASSEN :lol: (poste ich gleich aber erst eine Frage).
Ich habe mittlerweile herausgefunden, dass wenn ich nach der Procedur die den Thread erstellt eine Showmessage mache, alles korrekt abläuft. Scheint also ein sehr suspektes Zeitproblem zu sein. geht deine Vermutung in diese Richtung? |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Zitat:
|
AW: TList mit Record füllen und an weitere TList übergeben
Dein Problem ist offenbar nicht mehr die TList sondern die Synchronisierung von Threads.
In dem Fall wäre das dann auch ein Thema für einen neuen "Thread". ;-) Hier gab es kürzlich eine nette Zusammenfassung: ![]() |
AW: TList mit Record füllen und an weitere TList übergeben
Ich habe alles Unwichtige rausgenommen und ein bisschen Pseudo-Kommentiert, damit man es HALBWEGS nachvollziehen kann :lol:
Threads sind nicht das Problem. Denn als ich das alles ohne ObjectList hatte, lief es einwandfrei.
Delphi-Quellcode:
// aProcessFileList_ObjectList enthält "10" Instanzen von TFileEntry
// aProcessFileList_ObjectList.OwnsObjects ist "False" // aGlobalVars.iMaxCopyThreadCount ist "2" // iFilesPerThread ist "5" // Der ganze StringList-Kram ist nur zum TEST da {* Dateien z.B.: - Datei0.txt - Datei1.txt - Datei2.txt - Datei3.txt - Datei4.txt - Datei5.txt - Datei6.txt - Datei7.txt - Datei8.txt - Datei9.txt *} aTmpFileList_ObjectList := TObjectList.Create; aTmpFileList_ObjectList.OwnsObjects := False; try for i := 0 to aGlobalVars.iMaxCopyThreadCount - 1 do begin Application.ProcessMessages; try if aProcessFileList_ObjectList.Count >= iFilesPerThread then begin // Liste wird geleert, damit nicht die alten Daten drin sind / SEIN SOLLTEN aTmpFileList_ObjectList.Clear; iCnt := iFilesPerThread; if aProcessFileList_ObjectList.Count - iCnt = 1 then Inc(iCnt); {* Erster Durchgang: aTmpFileList_ObjectList bekommt "Datei0" bis "Datei4" Zweiter Durchgang: aTmpFileList_ObjectList bekommt "Datei5" bis "Datei9" *} for j := 0 to iCnt - 1 do aTmpFileList_ObjectList.Insert(j, aProcessFileList_ObjectList.Items[j]); sl := TStringList.Create; try {* Erster Durchgang: aus aProcessFileList_ObjectList wird "Datei4" bis "Datei0" gelöscht Zweiter Durchgang: aus aProcessFileList_ObjectList wird "Datei9" bis "Datei5" gelöscht *} for j := iCnt - 1 downto 0 do begin aProcessFileList_ObjectList.Delete(j); sl.Add(TFileEntry(aTmpFileList_ObjectList.Items[j]).sSourceDirItem); end; // StringList-TEST - was ist in der Liste? In eine Datei abspeichern! sl.SaveToFile(ExtractFilePath(ParamStr(0)) + '__DELETE' + IntToStr(i) + '.txt'); finally sl.Free; end; sl := TStringList.Create; try for j := 0 to aTmpFileList_ObjectList.Count - 1 do begin sl.Add(TFileEntry(aTmpFileList_ObjectList.Items[j]).sSourceDirItem); end; // StringList-TEST - was ist in der Liste? In eine Datei abspeichern! sl.SaveToFile(ExtractFilePath(ParamStr(0)) + '__' + IntToStr(i) + '.txt'); finally sl.Free; end; if aTmpFileList_ObjectList.Count > 0 then begin {* Erster Durchgang: Thread1 arbeitet "Datei0" bis "Datei4" ab Zweiter Durchgang: Thread2 arbeitet "Datei5" bis "Datei9" ab *} createCopyThread(aTmpFileList_ObjectList); end; end else break; except // ... end; end; // Wenn noch RESTE in der originalen ObjectList sind, dann erstelle damit einen seperaten Thread // Das ist z.B. der Fall, wenn 11 Dateien verarbeitet werden müssten. Die letzte (elfte) Datei würde hier in einen Thread gepackt werden if aProcessFileList_ObjectList.Count > 0 then begin createCopyThread(aProcessFileList_ObjectList); end; end; finally aTmpFileList_ObjectList.Free; end; //////////////////////////////////////////////////////////////////////////////// procedure createCopyThread(FaFileList: TObjectList); var aCopyThread: TCopyThread; begin aCopyThread := TCopyThread.Create; aCopyThread.aFileList := FaFileList; end; //////////////////////////////////////////////////////////////////////////////// type TCopyThread = class(TThread) private { Private-Deklarationen } FaFileList: TObjectList; protected procedure Execute; override; public { Public-Deklarationen } constructor Create; procedure Finish(Sender: TObject); property aFileList: TObjectList read FaFileList write FaFileList; end; |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
|
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Aber ich übergebe dem Thread doch die ObjectList. Hat der dann nicht eine vollkommen eigene Kopie davon? Und wenn ich dann das Original (aTmpFileList_ObjectList) ändere, dürfte das die Daten im Thread doch nicht ändern. |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Du könntest den Aufruf beim Thread als const definieren, dann wird eine Kopie deiner Liste erstellt:
Delphi-Quellcode:
procedure createCopyThread(const FaFileList: TObjectList);
... |
AW: TList mit Record füllen und an weitere TList übergeben
Wieso sollte da eine Kopie erstellt werden?
|
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Des Weiteren könntest du auch folgenden machen:
Delphi-Quellcode:
.
aTmpFileList_ObjectList := TObjectList<TFileEntry>.Create(False);
Das hat 2 Vorteile:
|
AW: TList mit Record füllen und an weitere TList übergeben
Das mit dem Zeiger wusste ich nicht. Ich dachte die ObjectList wird ganz normal übergeben und die Procedur hat dann davon eine Kopie.
Klingt jetzt verdächtig nach einem Array von ObjectListen wenn ich das richtig verstanden habe? Ihr seid so genial :-D Es funktioniert nun. Ich packe nun alle Instanzen in ObjectLists, die bei Gebraucht erzeugt werden. Verwaltet werden sie in einem dynamischen Array of TObjectList. |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Hier mal ein Beispiel wie ich es machen würde (ungetestet und nur hier runtergetippt):
Delphi-Quellcode:
try for i := 0 to aGlobalVars.iMaxCopyThreadCount - 1 do begin Application.ProcessMessages; // <--- Das hier brauchst du normalerweise nicht, da die Aktion so schnell abläuft, dass du davon sehr wahrscheinlich nichts mitbekommst try if aProcessFileList_ObjectList.Count >= iFilesPerThread then begin aTmpFileList_ObjectList := TObjectList<TFileEntry>.Create(False); // <-------------- ObjectList erst hier erzeugen. Somit kommen die sich nicht in die Quere. // aTmpFileList_ObjectList.OwnsObjects := False; // Durch den False Parameter entfällt diese Zeile // Liste wird geleert, damit nicht die alten Daten drin sind / SEIN SOLLTEN // aTmpFileList_ObjectList.Clear; // Das hier brauchst du normal auch nicht, da eine Liste beim Erstellen immer leer ist/sein sollte iCnt := iFilesPerThread; if aProcessFileList_ObjectList.Count - iCnt = 1 then Inc(iCnt); {* Erster Durchgang: aTmpFileList_ObjectList bekommt "Datei0" bis "Datei4" Zweiter Durchgang: aTmpFileList_ObjectList bekommt "Datei5" bis "Datei9" *} for j := 0 to iCnt - 1 do aTmpFileList_ObjectList.Add(aProcessFileList_ObjectList.Items[j]); CreateWorkingThread(aTmpFileList); end; finally // Blubb end; end; finally end;
Delphi-Quellcode:
Nicht hauen, wenn ich jetzt etwas vergessen oder mich irgendwo vertippt habe. :-D
procedure TWorkingThread.Execute;
begin // Do something here FTmpFileList.Free; // Hier die beim Erzeugen des Threads übergebene ObjectList freigeben end; |
AW: TList mit Record füllen und an weitere TList übergeben
Die Prozedur erhält eine Referenz auf die Objektliste.
Sie benutzt also die gleiche Liste. Eine Kopie der Liste könntest Du erzeugen, wenn Du eine neue Listeninstanz erzeugst und die Einträge kopierst. Wenn die Einträge Objekte sind, kopierst Du aber auch nur wieder Referenzen auf die Listeneinträge. Du kannst das analog
Delphi-Quellcode:
sehen.
Panel2 := Panel1
Hier wird der Variablen Panel2 ja auch nur eine andere Referenz (ein anderer Pointer) zugewiesen. PS: Sehe gerade, dass Du es gelöst hast. Aber Dein "Array of ObjectList" kann ich nicht ganz nachvollziehen. Egal, wenn es läuft, läuft es :-) Vielleicht kannst Du es ja später nochmal anders ordnen. |
AW: TList mit Record füllen und an weitere TList übergeben
Ich kapier es auch nicht. IMO ist der Knackpunkt, dass da in derselben Methode Threads erzeugt werden, die eine Objektliste zugewiesen bekommen, und diese Objektlisten gleich danach wieder freigegeben werden. Das dürfte den Thread nicht erfreuen, wenn er mitten in der Arbeit auf einen Dangling Pointer stößt.
|
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
|
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
SQL-Code:
Anzhappen:=Wieviele_Teile_in_Liste(FNList);
set length(LA,Anzhappen); for i:=0 to anzhappen-1 do begin LA[i]:=Tmpliste.create fülle(FNListe,LA[i]); end; For i:=0 to Anzhappen do machThread; So ungefähr sollte das Laufen. Gruß K-H |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Da weiß man was man benutzt! Gruß K-H |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Dann braucht er auch kein Array mehr mitzuschleppen in dem Dann u.U. die Instanzen noch hängen bleiben. |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
|
AW: TList mit Record füllen und an weitere TList übergeben
Das Geld reicht nur für XE2.
Umständlich ist mein zweiter Vorname. Ich habe erst letztens noch eine Funktion komplett neugeschrieben. Sie wurde insgesamt sehr viel schneller. Bei 1.000.000 Aufrufen spare ich 200ms. Ich brauche diese Funktion exakt ein einziges mal in meinem Code. Hat sich demnach also gelohnt :thumb: |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
OK Dann fällt alles mit der Parallel Library weg... Ich würde für sowas eine ![]() der 1. Task schon los legen und die Daten verarbeiten... Also eine Threadsave Queue verwenden in die Du die einzelnen File-Record-Classen rein pumpst... Am besten die Klasse als
Delphi-Quellcode:
aufbauen und nur die Interfaces in die Queue packen...
TFileData = Class(TInterfacedObject,ICanHandeFileData)
Dann können sich N-Thread daraus bedienen und brauchen einfach nur das Interface los zu lassen... Da die Klasse referenzcounted ist, ist auch Dein Speicher wieder sauber... Mavarik :coder: |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
Gruß K-H |
AW: TList mit Record füllen und an weitere TList übergeben
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:29 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz