AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Thread ist mit einer Aufgabe fertig: Mainthread mitteilen
Thema durchsuchen
Ansicht
Themen-Optionen

Thread ist mit einer Aufgabe fertig: Mainthread mitteilen

Ein Thema von xZise · begonnen am 2. Apr 2008 · letzter Beitrag vom 3. Apr 2008
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von xZise
xZise

Registriert seit: 3. Mär 2006
Ort: Waldbronn
4.303 Beiträge
 
Delphi 2009 Professional
 
#1

Thread ist mit einer Aufgabe fertig: Mainthread mitteilen

  Alt 2. Apr 2008, 23:13
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
Fabian
Eigentlich hat MS Windows ab Vista den Hang zur Selbstzerstörung abgewöhnt – mkinzler
  Mit Zitat antworten Zitat
Dax
(Gast)

n/a Beiträge
 
#2

Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile

  Alt 2. Apr 2008, 23:19
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.
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#3

Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile

  Alt 2. Apr 2008, 23:26
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.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Benutzerbild von xZise
xZise

Registriert seit: 3. Mär 2006
Ort: Waldbronn
4.303 Beiträge
 
Delphi 2009 Professional
 
#4

Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile

  Alt 3. Apr 2008, 00:26
Zitat von Dax:
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.
Sorry Dax, aber das klingt etwas "konfus"
Also ich habe einen Mainthread (MT) und einen Downloadthread (DT).
Der DT macht also folgendes:
  • CS.Enter
  • Sync(MTMethode)
  • "Liste wartender Threads?"
  • CS.Leave
  • Suspend

Aber was ich möchte ist ja folgendes:
Code:
DT
 |
 |
 |
 Download fertig     MT
 |------------------> Aha :) DL fertig
 Weitere Datei        | (Ein paar Sachen machen)
 downloaden (wenn da) | 
 v                    v
Wobei alles was dort beim MT abläuft im Kontext von "Sync" aufgerufen würde.

Zitat von Luckie:
Wie erzeugst du den Thread? Mit der TThread-Klasse der VCL oder mit BeginThread?
Es ist die VCL TThread-Klasse.

Zitat von Luckie:
Wenn ersteres, die Thread-Klasse kennt das Ereignis OnTerminate, welches aufgerufen wird, wenn die Execute Methode verlassen wird.
Theoretisch wird sie nie verlassen
Nur wenn das Programm beendet wird.

Aktuell sieht der Code folgendermassen aus:
Delphi-Quellcode:
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;
Wie man sieht, habe ich mehrere Schleifen.
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
Fabian
Eigentlich hat MS Windows ab Vista den Hang zur Selbstzerstörung abgewöhnt – mkinzler
  Mit Zitat antworten Zitat
busybyte

Registriert seit: 15. Sep 2006
165 Beiträge
 
#5

Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile

  Alt 3. Apr 2008, 01:04
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)
I love DiscCat
  Mit Zitat antworten Zitat
Dax
(Gast)

n/a Beiträge
 
#6

Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile

  Alt 3. Apr 2008, 01:11
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
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#7

Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile

  Alt 3. Apr 2008, 07:42
Zitat von Dax:
CS.Enter
MT.Daten := irgendwas
Synchronize(TeilFertig)
Aufgabe := Dequeue(Aufgaben)
CS.Leave
Du mischst hier zwei verschiedene Syncronisationsmethoden. Das führt leicht zu einem Stillstand der Anwendung.

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:
type TInfo=....
     PInfo=^TInfo;
Im DownloadThread:
Delphi-Quellcode:
//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)
Damit hast du deine Information in den Heap gelegt und kannst diese im Mainthread asynchron auslesen.
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;
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#8

Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile

  Alt 3. Apr 2008, 07:43
Zitat von xZise:
Zitat von Luckie:
Wenn ersteres, die Thread-Klasse kennt das Ereignis OnTerminate, welches aufgerufen wird, wenn die Execute Methode verlassen wird.
Theoretisch wird sie nie verlassen
Nur wenn das Programm beendet wird.
Es steht dir doch frei eigene Ereignisse zu deklarieren. Desweiteren kannst du auch mit der Thread-Klasse Nachrichten nutzen. Ist nur nicht so schön, da es in diesem Fall irgendwie das OOP Konzept verletzt.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#9

Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile

  Alt 3. Apr 2008, 08:10
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 Workerthreadpool-Klasse verwenden willst. Ich persönlich finde die Unterteilung in einzelne Jobs übersichtlich, zumal Du mit meiner Threadpoolklasse mehrere Dateien gleichzeitig geladen werden können.
Das wäre Alles, was Du implementieren müsstest:
Delphi-Quellcode:
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;
Und dann einfach:
Delphi-Quellcode:
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]));
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.

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.
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Benutzerbild von xZise
xZise

Registriert seit: 3. Mär 2006
Ort: Waldbronn
4.303 Beiträge
 
Delphi 2009 Professional
 
#10

Re: Thread ist mit einer Aufgabe fertig: Mainthread mitteile

  Alt 3. Apr 2008, 13:08
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.
[/edit][/equote:e73dcd2f3f]

MfG
xZise

PS: Zum Threadpool: Eigentlich wollte ich mit dem Thread nur umgehen, dass die Anwendung blockiert wird.
Fabian
Eigentlich hat MS Windows ab Vista den Hang zur Selbstzerstörung abgewöhnt – mkinzler
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:53 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz