![]() |
Thread ist mit einer Aufgabe fertig: Mainthread mitteilen
Hallo DP,
ich habe ein kleines Problem :) Und zwar löse ich z.Zt. ein Problem Multithreaded. Dazu habe ich einen Thread, der eine Liste von Dateien abarbeitet. Nun soll dieser dem Mainthread mitteilen, sobald ein Download fertig ist. Entweder ich polle mit dem Mainthread (=> Unschön, da unnötige Auslastung) ob Aufgaben fertig sind, oder der Thread schickt irgendwie eine Nachricht an den Mainthread. Das Problem ist das der Thread nicht aufhören soll zu arbeiten, sondern sozusagen nur der Liste sagt: "Bin mit der Datei XYZ fertig" und dann die Liste weiter abarbeitet. Also ist "Syncronize" ganz schlecht ;) Was sich anbieten würde, wären Messages, wobei ich da zwei Probleme hätte. Erstens: Wie schicke ich eine ab, so dass ich die Informationen übermittle und ohne verwechslungen zu begegnen. Zweitens: Wie empfange ich diese? Bestimmt einfach zu lösen. ^^ MfG xZise |
Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile
Du kannst Synchronize benutzen, das sollte kein Problem darstellen. Dazu schreibst du dir eine Methode in die Threadklasse, die - in einer Critical Section - beim Hauptthread anklopft, sich selbst in die Liste wartender Threads einfügt und sich dann schlafen legt, nachdem du die CS geschlossen und über Synchronize dem Hauptthread Bescheid gesagt hast, dass da was wartet. Der Hauptthread schnappt sich nun die Liste und verteilt neue Aufgaben an die Threads, weckt sie wieder auf und das Spiel geht von vorne los.
|
Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile
Wie erzeugst du den Thread? Mit der TThread-Klasse der VCL oder mit BeginThread? Wenn ersteres, die Thread-Klasse kennt das Ereignis OnTerminate, welches aufgerufen wird, wenn die Execute Methode verlassen wird. Arbeitest du mit BeginThread, dann schick eine Nachricht an dein Hauptthread (const TM_FINISH = WM_USER + 1). Abfangen kannst du die Nachricht, in dem du die WndProc deiner Form überschreibst und gezielt auf deine Nachricht reagierst.
Delphi-Quellcode:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } procedure WndProc(var Message: TMessage); override; end; var Form1: TForm1; const TM_FINISH = WM_USER +1 ; type TThreadParams = record FHandle: THandle; end; PThreadParams = ^TThreadParams; implementation {$R *.dfm} function Thread(p: Pointer): Integer; var Handle: THandle; begin Handle := PThreadParams(p)^.FHandle; SendMessage(Handle, TM_FINISH, 0, 0); Result := 0; Dispose(p); end; procedure TForm1.WndProc(var Message: TMessage); begin inherited; case Message.Msg of TM_FINISH: ShowMessage('Fertig'); end; end; procedure TForm1.Button1Click(Sender: TObject); var tp: PThreadParams; ThreadID: Cardinal; begin New(tp); tp.FHandle := Handle; CloseHandle(BeginThread(nil, 0, @Thread, tp, 0, ThreadID)); end; end. |
Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile
Zitat:
Also ich habe einen Mainthread (MT) und einen Downloadthread (DT). Der DT macht also folgendes:
Aber was ich möchte ist ja folgendes:
Code:
Wobei alles was dort beim MT abläuft im Kontext von "Sync" aufgerufen würde.
DT
| | | Download fertig MT |------------------> Aha :) DL fertig Weitere Datei | (Ein paar Sachen machen) downloaden (wenn da) | v v Zitat:
Zitat:
Nur wenn das Programm beendet wird. Aktuell sieht der Code folgendermassen aus:
Delphi-Quellcode:
Wie man sieht, habe ich mehrere Schleifen.
procedure TDownloadThread.Execute;
var i, j: Integer; fs : TFileStream; fn : string; breaked : Boolean; FDownload : TIdHTTP; FSource, FDestination : string; FExtract : Boolean; begin // Initiate Downloader FDownload := TIdHTTP.Create(nil); try // Searching until stopped while true do begin try // Searching all entrys i := 0; while i < FList.Count do begin // Searching threadsafe FList.EnterCritical; try // Searching for a ready entry or end of the list while (i < FList.Count) and (FList[i].Status <> dsReady) do Inc(i); if i < FList.Count then begin // Found a file to download [...] // Only downloadcode // Send message to Mainthread: Download complete end; finally FList.LeaveCritical; end; end; finally Suspend; end; end; finally FreeAndNil(FDownload); end; end; Die äußerste sorgt dafür, dass durch "Resume" der Thread immer wieder angeworfen werden kann. Die nächst innere sorgt dafür dass er solange sucht, bis er alle Einträge durch ist. Die innerste sorgt dann dafür dass diejenigen übersprungen werden, die gedownloadet werden, gedownloadet sind, oder noch nicht fertig sind. Ich habe mich dabei für drei Schleifen entschieden, da ich somit einfach mit den CriticalSections verhindern kann, dass während der Suche nicht an der Liste gearbeitet wird. MfG xZise |
Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile
Aber Dax hat recht,ohne polling gehts's meiner Meinung nach nur über eine Methode, was aber im Endeffekt beim Hauptthread einem Polling gleich kommt
und der Geschwindigkeitsverlust ist zu vernachlässigen, zumal es sich um einen Download handelt.Die meiste Geschwindigkeit wird, vom Download und der Programierart abgesehen,bei der grafischen Darstellung der Daten verbraucht.(Refresh,Update,Processmessages usw.,weisst Du sicher selbst) |
Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile
Ich meine es so:
Downloadthread: CS.Enter MT.WartendeThreads.Add(self) CS.Leave Synchronize(MT.NeueAufgabe) Suspend Hauptthread, NeueAufgabe: for each (wartender Thread) do neue Aufgabe zuteilen Resume Ansonsten kannst du auch eine Liste mit herunterzuladenden Dateien bereitstellen und es so lösen: Downloadthread: CS.Enter MT.Daten := irgendwas Synchronize(TeilFertig) Aufgabe := Dequeue(Aufgaben) CS.Leave Wie das TeilFertig-Dings im Hauptthread aussieht, überlasse ich dir ;) |
Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile
Zitat:
Ohne zu bewerten, welche Lösung für dich geeigneter ist, möchte ich dir noch deine eingangs erwähnte Methode über Messages vorstellen: Du nimmst dir einfach eine dynamische Variable.
Delphi-Quellcode:
Im DownloadThread:
type TInfo=....
PInfo=^TInfo;
Delphi-Quellcode:
Damit hast du deine Information in den Heap gelegt und kannst diese im Mainthread asynchron auslesen.
//const myMessage=WM_User;
procedure xyz.execute; var Info:PInfo; ... //zum senden new(Info) Info^:=.... if not postmessage(FMainHandle,myMessage,0,integer(Info)) then dispose(Info); //Message nicht erfolgreich (sollte nicht auftreten)
Delphi-Quellcode:
//zur einfacheren Handhabung wäre noch ein eigener Message-Typ möglich:
type TDTMessage=packed record Msg:Cardinal; WParam:LongInt; Info:PInfo; Result:LongInt; end; type TMainForm=... procedure getDownloadMessage(var msg:TDTMessage); message myMessage; //oder procedure getDownloadMessage(var msg:TMessage); message myMessage; ... procedure TMainform.getDownloadMessage(var msg:TDTMessage); begin try irgendetwas:=msg.Info^; finally dispose(msg.Info); end; end; |
Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile
Zitat:
|
Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile
Dafür eignet sich eine Jobliste doch ganz gut. Pro Datei ein Job. Wenn ein Job fertig ist, teilt er dies dem Hauptthread mit.
Du kannst ja mal schauen, ob Du meine ![]() Das wäre Alles, was Du implementieren müsstest:
Delphi-Quellcode:
Und dann einfach:
Type
TDownLoadJob = Class (TWorkerThreadJob) private fMyFile : String; protected Procedure Execute(aThread: TWorkerThread); Override; Public Constructor Create (aFileName : String); End; Constructor TDownloadJob.Create (aFileName : String); Begin Inherited Create; fMyFile := aFileName; // UsesCOMObjects := True // , falls Du eben COM verwendest End; Procedure TDownloadJob.DoShowFinished; Begin MainForm.Memo1.Lines.Add(Format('Download der Datei %s beendet',[fMyFile])); End; Procedure TDownloadJob.Execute (aThread : TWorkerThread); Begin DoDownloadFile(fMyFile); aThread.Synchronize (DoShowFinished); End;
Delphi-Quellcode:
Den Rest erledigt der Threadpool. Die paar Millisekunden, die bei der Rückmeldung verloren gehen, machen rein gar nichts, weil wir ja einen Threadpool haben.
ThreadPool := TWorkerThreadPool.Create(10); // Erzeugt 10 Threads, die die Jobs parallel abarbeiten
ThreadPool.OnJobException := HandleFinishedDownloads; // 'Exceptionhandler' zuweisen For i:=0 to slFileList.Count-1 do ThreadPool.AddJob (TDownloadJob.Create (slFileList[i])); Wenn Du das aber selber basteln willst, und Dein Download-Thread auf gar keinen Fall Synchronize aufrufen soll, dann müsstest Du einen zweiten Thread erzeugen, der eine Stringliste und eine Semaphore verwaltet (Oder Messages, is aber -finde ich- uncool). Dieser 2. Thread wartet also nur darauf, das der Download-Thread ihm etwas in seine Stringliste schreibt und die Semaphore anzuppelt, woraufhin dieser dann kurz aufwacht und schön gemächlich per Synchronize dem Hauptthread die Nachricht(en) in eine Listbox, Memo oder sonstwohin schreibt. Das ginge auch mit Suspend/Resume, aber das mag ich nicht. Edit: Die erwähnte Workerthreadpool-Klasse unterstützt nun die Benachrichtigung aus einem Job heraus, denn meine o.g. Methode bindet den Job ja an das Formular (über die DoShowFinished-Methode), und das ist nicht gut. |
Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile
Abgesehen von dieser Threadpool sache:
Was macht "Sync" genau? Ich habe gedacht, dass dies im Kontext des anderen Threads eine Methode ausführt, und solange wartet bis diese Methode ausgeführt ist. Und genau das ist mein Problem: Es kann sein, dass diese Methode die aufgerufen wird, lange brauchen kann. Und genau dann wäre es sinnvoll, dass der Thread einfach weiterarbeiten kann. Oder arbeitet "Sync" anders? Und initiert nur die ausführung des Codes im Kontext des anderen Threades, und arbeitet selber währenddessen weiter? Edit von xZise um 14:16 am 03.04.2008 [edit=0]In der Hilfe steht folgendes: [equote:e73dcd2f3f="Hilfe zu "Synchronize"]Der Thread wird unterbrochen, während die angegebene Methode im Haupt-Thread ausgeführt wird. MfG xZise PS: Zum Threadpool: Eigentlich wollte ich mit dem Thread nur umgehen, dass die Anwendung blockiert wird. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:58 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 by Thomas Breitkreuz