![]() |
Re: Threads verwalten
InfoRecord ist ok. (Um noch sicherer zu gehen, sollte man auf den Rückgabewert von PostMessage reagieren, sonst läuft man Gefahr, dass ein MemLeak entsteht)
Aber nicht Mainform.Handle verwenden. Lieber das Handle mit an den Thread übergeben und dort aus privates Feld speichern. |
Re: Threads verwalten
Zwischenfrage:
Da PostMessage die Nachricht in die Warteschlage ablegt, kann der Rückgabewert doch nur den Erfolg der Nachrichten-Ablage verkünden - damit wird nicht ausgesagt, dass der Empfänger sie bereits erhalten hat - korrekt?? |
Re: Threads verwalten
Zitat:
Wenn der Empfänger allerdings die Nachricht einfach nur wegschmeißt oder vor dem Dispose eine Exception auftritt (->> besser try finally verwenden), kann man bei dieser Art Kommunikation nichts machen. |
Re: Threads verwalten
Soweit bin ich jetzt:
Delphi-Quellcode:
Das Ganze funktioniert schon ganz gut nur die Speicherung und das Logging muß jetzt noch rein.
const
WM_MY_USER = WM_USER + 101; type PInfoRecord = ^TInfoRecord ; TInfoRecord = record GUID : string; Meldung : string; end; TWatchThread = class(TThread) private fHandle : HWND; procedure fisFileNotifyDirectoryChanged(Sender: TObject); public OneBackup : TOneBackup; FileNotify : TfisFileNotification; FileZip : TJvZlibMultiple; procedure StartWatch; protected procedure DoTerminate; override; public constructor Create(aOwner : TComponent; Handle : HWND); virtual; end; TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Button2: TButton; Edit1: TEdit; Button3: TButton; procedure OnEvent( var Message : TMessage ); message WM_MY_USER; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button3Click(Sender: TObject); private ObjectList : TObjectList; public end; var Form1: TForm1; implementation {$R *.dfm} procedure TWatchThread.DoTerminate; begin inherited; FileNotify.Stop; end; procedure TWatchThread.fisFileNotifyDirectoryChanged(Sender: TObject); var InfoRecord : PInfoRecord; begin if not Terminated then begin New(InfoRecord); InfoRecord^.GUID := OneBackup.GUID; InfoRecord^.Meldung := OneBackup.Name + ' - ' + FormatDateTime('hh:nn:ss.zzz', now) + ' - ' + 'FileNotify.MessageNo'; PostMessage(fHandle, WM_MY_USER, 1, Integer(InfoRecord)); // hier müssen die Änderungen abgespeichert werden // funktioniert noch nicht // FileZip.CompressDirectory(OneBackup.WatchFolder, // OneBackup.IncludeSub, // OneBackup.BackupFolder + '\' + OneBackup.GUID + '.zip'); end; end; procedure TWatchThread.StartWatch; begin FileNotify.Subtree := OneBackup.IncludeSub; FileNotify.Start; end; constructor TWatchThread.Create(aOwner : TComponent; Handle : HWND); var NewGUID: TGUID; begin inherited Create(true); freeOnTerminate := true; fHandle := Handle; OneBackup := TOneBackup.Create; FileNotify := TfisFileNotification.Create(aOwner); FileNotify.OnDirectoryChanged := fisFileNotifyDirectoryChanged; with OneBackup do begin CreateGUID(NewGUID); GUID := GUIDToString(NewGUID); Name := ''; WatchFolder := 'C:\Test\Treadtest'; // nur mal zum Testen BackupFolder := 'C:\Test'; // nur mal zum Testen ... FileNotify.Directory := WatchFolder; FileNotify.NotificationType := [noFilename, noDirname, noSize, noLastWrite, noCreation]; end; FileZip := TJvZlibMultiple.Create(aOwner); end; procedure TForm1.Button1Click(Sender: TObject); var WatchThread : TWatchThread; begin WatchThread := TWatchThread.Create(Self, Self.Handle); // hier die Daten übergeben WatchThread.OneBackup.Name := Edit1.Text; TWatchThread(ObjectList.Items[ObjectList.Add(WatchThread)]).Start; end; procedure TForm1.Button2Click(Sender: TObject); var i : integer; begin for i := 0 to ObjectList.Count - 1 do begin TWatchThread(ObjectList.Items[i]).Terminate; end; end; procedure TForm1.Button3Click(Sender: TObject); begin TWatchThread(ObjectList.Items[ObjectList.Count - 1]).StartWatch; end; procedure TForm1.FormCreate(Sender: TObject); begin ObjectList := TObjectList.Create; ObjectList.OwnsObjects := false; end; procedure TForm1.FormDestroy(Sender: TObject); begin ObjectList.Free; end; procedure TForm1.OnEvent(var Message : TMessage); var InfoRecord : PInfoRecord; ID : string; begin case Message.Msg of WM_MY_USER : begin try InfoRecord := PInfoRecord(Message.LParam); ID := InfoRecord^.GUID; Memo1.Lines.Add(InfoRecord^.Meldung); finally Dispose(InfoRecord); end; end; end; end; Ich werde aber die Komponenten für FileNotify und FileZip nochmal austauschen. Die Gründe: - TfisFileNotification sagt mir nicht welche Dateien geändert wurden, mit SHChangeNotify geht das - TJvZlibMultiple scheint mir zu unflexibel (bzw. kein kompatibles Zipformat), mit TAbZipKit geht es vielleicht besser Ich muß mir auch noch eine Struktur überlegen wie ich die Änderungen speichere. Jetzt erst einmal werde ich, bei geder Änderung, alle Dateien in eine neue Zip-Datei speichern. Das ist zwar nicht so schön, aber reicht für meine Zwecke. Ich will ja nur den Fortschritt meiner Diplomarbeit dokumentieren und sichern und das möglichst auf einem USB-Stick/HDD. Ich benutze TeXnicCenter und MiKTeX 2.8 in einem Mojo-Pack (das geht erstaunlich gut), solange keine vernünftige Portable Version raus ist und werde die tex-Dateien mit dem kleinen Programm zusätzlich auf dem USB-Stick speichern und zu Hause auf den Rechner kopieren. :) Das sollte reichen. |
Re: Threads verwalten
Hallo David Martens.
Also folgendes. >> New(InfoRecord); Ist nur innerhalb des Threadkontext (Threadobjekt) gültig. Es handelt sich um lokalen Speicher, dies funktioniert bei deinem Source nur weil Du keinen Thread erzeugst, Wo ist das Execute??? Für Globalen Memory musst Du zB. GlobalAlloc verwenden, dieser Memory kann dann auch vom MainThread der Applikation ohne Probleme verwendet werden. >> PostMessage(fHandle, WM_MY_USER, 1, Integer(InfoRecord)); Ist nur solange in einer multithreaded Umgebung Gültig, solange kein weiteres Event kommt. ZB. Event wird vom OS getriggert, thread hat noch Rechenzeit übrig, und es kommt innerhalb dieser noch zu einem zusätzlichen Event, dann überschreibst Du den vorhergehenden Record, da Du diesen Asynchron mit PostMessage in die MQ gepostet hast. Hier musst Du Send Message verwenden. >> FileZip := TJvZlibMultiple.Create(aOwner); Hier musst Du berücksichtigen, dass VCL-Komponenten, die Du im Mainthread erzeugst, nicht Threadsave sind! Syncronize, bzw. Sperrobjekte verwenden! >> ObjectList : TObjectList;
Delphi-Quellcode:
Hier hilft Terminate alleine nicht, da du blockieren auf ein Event wartest, hier musst du nach demfor i := 0 to ObjectList.Count - 1 do begin TWatchThread(ObjectList.Items[i]).Terminate; end; Terminate, noch das FileNotify - Handle schließen, sonst terminiert der Thread nicht. Sieh Dir am besten mal das Thread Demo das mit Delphi mitgeliefert wird an. lg. |
Re: Threads verwalten
Zitat:
Selbst in den Delphi-Sourcen wird gelegentlich dieser Weg gegangen. Nichtsdestotrotz gibt es bessere Varianten, aber da kann man bei Threads ewig streiten. Zitat:
Zitat:
Zitat:
Wo ich ein Problem sehe, ist:
|
Re: Threads verwalten
Hallo sirius, hast Recht, war da total auf dem Holzweg (oder sonst wo?)!!!
Bei der >> ObjectList : TObjectList;, hab ich aber nicht das SendMessage sondern das FileNotyfy Event gemeint, dies Blockiert ja, wenn sich im Filesystem nichts ändert. Dh. Thread ist Terminated, aber dieser kann sich nicht beenden wenn das FileNotify-Handle nicht geschlossen wird. @David Martens hab ein tolles Tutorial gefunden, hab mich draus auch mal schlau gemacht "g"! ![]() lg. |
Re: Threads verwalten
Zitat:
--> also irgendiwe muss die Blockierung gelöst werden. |
Re: Threads verwalten
Danke erstmal für die tolle Unterstützung.
Ja, ich hab vergessen die Objekte freizugeben. Wo mach ich das am besten im DoTerminate oder destructor von TWatchThread? Ich würde die in den destructor packen, weil ich sie ja auch im constructor erstelle. Zu den beiden VCL-Komopnenten, die sind nicht visuell (auch die Alternativen). @sirius: 1. Warum muß das Filexxx.Stop eventuell synchronisiert werden? Das sagt der Komponente nur das es aufhören soll Events auszulösen, wennn an dem beoachteten Ordner Änderungen vorgenommen werden. Das sollte nicht zeitkritisch sein. 2. Warum soll ich das Erstellen der Komponenten ins Execute packen? Beim Erstellen passiert ja noch nicht viel, erst beim Starten (Filexxx.Start und Starten des Zipvorganges) legen die los. @uoeb7gp: Zitat:
Das fisFileNotifyDirectoryChanged soll ja nur die Meldung für das Log absetzen und den Backup-Vorgang starten. Später kommen dann noch weitere Funktionen hinzu (detaillierter Datei(namen)test und Meldungen für Backup-start und Backup-erfolgreich/abgebrochen) |
Re: Threads verwalten
Zitat:
Zitat:
Diese Könnte neben den Fenstern auch noch bei anderen Systemressourcen der Fall sein. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:07 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