Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Software-Projekte der Mitglieder (https://www.delphipraxis.net/26-software-projekte-der-mitglieder/)
-   -   AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi (https://www.delphipraxis.net/82530-asynccalls-2-91-asynchrone-funktionsaufrufe-delphi.html)

jbg 15. Dez 2006 00:10


AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Liste der Anhänge anzeigen (Anzahl: 1)
Mit meiner neuesten Erschaffung (AsyncCalls) kann man nun sehr leicht und ohne großen Aufwand mehreren Funktionen und Methoden zur gleichen Zeit ausführen und diese auch noch synchronisieren. Dazu ist nicht mehr notwendig, als eine der AsyncCall() Funktionen aufzurufen. Diese liefert dann ein IAsynCall Interface zurück mit dem der asynchrone Funktionsaufruf wieder synchronisiert werden kann. Da es sich um ein Interface handelt, wird beim Verlassen der Funktion/Methode auf alle noch ausstehenden asynchronen Funktionen gewartet.

Homepage, Download und Beschreibung

Beispielcode (Einlesen von drei Verzeichnissen in asynchronen Funktionen):
Delphi-Quellcode:
function TFormMain.DoSomething(Value: TObject): Integer;
begin
  Result := 0;
end;

procedure TFormMain.Button3Click(Sender: TObject);
var
  Value: Integer;
begin
  // TAsyncCalls.Invoke<TObject>(DoSomething, nil); wegen internal compiler error nicht mehr funktionsfähig

  TAsyncCalls.Invoke(procedure
  begin
    Value := 10;
    TAsyncCalls.VCLInvoke(procedure
    begin
      ShowMessage('Der Werk könnte ungleich 10 sein: ' + IntToStr(Value));
    end);
    Value := 20;
    TAsyncCalls.VCLSync(procedure
    begin
      ShowMessage('Der Wert ist 20: ' + IntToStr(Value));
    end);
    Value := 30;
  end).ForceDifferentThread;

  Sleep(1000);
end;
Delphi-Quellcode:
{ Die cdecl Funktion GetFiles() besitzt zwei Parameter vom Typ string und object, die hier ganz normal deklariert werden. }
procedure GetFiles(const Directory: string; Filenames: TStrings); cdecl;
var
  h: THandle;
  FindData: TWin32FindData;
begin
  h := FindFirstFile(PChar(Directory + '\*.*'), FindData);
  if h <> INVALID_HANDLE_VALUE then
  begin
    repeat
      if (StrComp(FindData.cFileName, '.') <> 0) and (StrComp(FindData.cFileName, '..') <> 0) then
      begin
        Filenames.Add(Directory + '\' + FindData.cFileName);
        if FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0 then
          GetFiles(Filenames[Filenames.Count - 1], Filenames);
      end;
    until not FindNextFile(h, FindData);
    Windows.FindClose(h);
  end;
end;

procedure TFormMain.ButtonGetFilesClick(Sender: TObject);
var
  Dir1, Dir2, Dir3: IAsyncCall;
  Dir1Files, Dir2Files, Dir3Files: TStrings;
begin
  Dir1Files := TStringList.Create;
  Dir2Files := TStringList.Create;
  Dir3Files := TStringList.Create;
  ButtonGetFiles.Enabled := False;
  try
    { Die beiden Parameter der GetFiles() Funktion werden mittels eines const array of const übergeben }
    Dir1 := AsyncCall(@GetFiles, ['C:\Windows', Dir1Files]);
    Dir2 := AsyncCall(@GetFiles, ['D:\Html', Dir2Files]);
    Dir3 := AsyncCall(@GetFiles, ['E:', Dir3Files]);

    { Warten bis die beiden asynchronen Funktion beenden sind. Während dieser Zeit dem die UI nicht einfrieren lassen }
    while AsyncMultiSync([Dir1, Dir2], True, 10) < 0 do
      Application.ProcessMessages;
    Dir3.Sync; // Auf die Beendigung der Dir3 Funktion warten

    MemoFiles.Lines.Assign(Dir1Files);
    MemoFiles.Lines.AddStrings(Dir2Files);
    MemoFiles.Lines.AddStrings(Dir3Files);
  finally
    ButtonGetFiles.Enabled := True;
    Dir3Files.Free;
    Dir2Files.Free;
    Dir1Files.Free;
  end;
end;

Mavarik 15. Dez 2006 07:50

Re: AsyncCalls - Asynchrone Funktionsaufrufe in Delphi
 
Hallo!

OK...

Du hast also eine Unit geschrieben, die eigene Threads erzeugt?

Kannst Du nochmal erklären, wofür und was das bringen soll?

Was ist mit dem Sync für Ausgaben? Was ist mit "Threadsave Code"?

Sorry, vielleicht brauche ich heute morgen auch nur erst mal einen Kaffee... :-)

Grüsse Frank

PS.: Klingt aber spannend....

jbg 15. Dez 2006 12:13

Re: AsyncCalls - Asynchrone Funktionsaufrufe in Delphi
 
Zitat:

Zitat von Mavarik
Du hast also eine Unit geschrieben, die eigene Threads erzeugt?

Ja ich habe eine Unit geschrieben. Was verstehst du unter "eigene Threads erzeugen"? Natürlich nutzt AsynCalls Threads. Denn ansonsten würde die Asynchronität nicht funktionieren.

Zitat:

Kannst Du nochmal erklären, wofür und was das bringen soll?
Wenn man z.B. zwei Dateien downloaden und verarbeiten will kann man das wie folgt lösen:
Delphi-Quellcode:
DownloadFile('http://sonstwo.de/file1.html', Stream);
Verarbeite(Stream);
DownloadFile('http://sonstwo.de/file2.html', Stream2);
Verarbeite(Stream2);
Da das Netzwerk aber nicht besonders schnell ist, verbringt man eine ganze Menge an Zeit untätig wenn der erste Download ewig dauert, wobei der zweite recht schnell ist.

Mit AsyncCalls kann man das z.B so schreiben (wobei das sicherlich nicht der Weisheit letzter Schluss ist):
Delphi-Quellcode:
procedure DownloadFileAsync(const Url: string; Stream: TStream); cdecl;
begin
  DownloadFile(Url, Stream);
end;

for i := 0 to Urls.Count - 1 do
begin
  // beide Downloads starten
  a := AsyncCall(@DownloadFileAsync, [Urls[i], Streams[0]]);
  b := AsyncCall(@DownloadFileAsync, [Urls[i], Streams[1]]);

  Index := AsyncWaitSync([a, b], False); // warten bis mindestens ein AsyncCall beendet ist

  Verarbeite(Streams[Index]); // den ersten beendeten Download verarbeiten
  AsyncWaitSync([a, b], True); // auf den anderen Download warten
  Verarbeite(Streams[(Index + 1) mod 2]);
end;
Syntaktische Korrektheit ist nicht gegeben


Zitat:

Was ist mit dem Sync für Ausgaben?
Wenn du das Ergebnis einer async. Funktion benötigst, dann muss man einfach IAsyncCall.Sync aufrufen und bekommt das Ergebnis (sofern vorhanden => procedures haben einen undefinierten Rückgabewert)

Zitat:

Was ist mit "Threadsave Code"?
VCL Code sollte man nicht in einer async. Funktion aufrufen. Alle Arg-Parameter von AsyncCall() werden intern kopiert, womit sie in den async. Funktionen ohne bedenken benutzt werden können. Die Ausnahme bilden hier die AsynCallEx() Funktionen, die einen veränderbaren Record (Value Type) an die async. Funktion weiterleiten.
Delphi-Quellcode:
type
  TData = record
    s: string;
    i: Integer;
  end;

procedure DoSomething(var Data: TData);
begin
  Data.s := 'Hallo';
  Data.i := 10;
  Sleep(100); // ein wenig Zeit verplempern.
end;

var
  d: TData;
  a: IAsyncCall;
begin
  a := AsyncCallEx(@DoSomething, d);
  // irgendwas anderes machen ...
  a.Sync;
  ShowMessage(d.s + ': ' + IntToStr(d.i));
end;

Arthur Hoornweg 15. Dez 2006 13:02

Re: AsyncCalls - Asynchrone Funktionsaufrufe in Delphi
 
Eine Sache verstehe ich nicht (oder vielleicht sehe ich ein Problem
das es gar nicht gibt).

Du übergibst die Parameter als ein Array of Const.
Ich weiss nicht mit Sicherheit wie Delphi so ein Array verwaltet
(kopiert er etwa alles auf den Stack?) aber üblicherweise ist ein
"const" Parameter ein Pointer.

VAR x:tstringlist;
begin
x:=tstringlist.create;
x.add('test');
AsyncCall (@meinproc, [X]);
x.add('Hallo');



bekommt "meinproc" nun 1 oder 2 Strings zu verdauen?

jbg 15. Dez 2006 18:39

Re: AsyncCalls - Asynchrone Funktionsaufrufe in Delphi
 
Zitat:

Zitat von Arthur Hoornweg
bekommt "meinproc" nun 1 oder 2 Strings zu verdauen?

Objekte werden natürlich nicht kopiert. Bei denen musst du dann schon selbst dafür sorgen, dass nicht zwei gleichzeitig darauf zugreifen. Das ist auch bei normalen Funktion der Fall. Was AsyncCall(func, []) anders macht, ist dass es elementare Typen (Char, Integer, ...) sowie Referenz-gehzählte Typen (String, Interface, ...) sicher macht (es wird intern eine Zuweisung an eine Variable des selben Types gemacht, der während des Funktionsaufrufs existent bleibt. Wenn du mehr erfahren willst, was da intern abläuft, dann solltest du einen Blick in die CopyVarRec() Funktion in AsyncCalls.pas werfen.

Zitat:

aber üblicherweise ist ein "const" Parameter ein Pointer.
Das ist er auch weiterhin. Nur zeigt der Zeiger halt nicht auf das Original, sondern auf eine interne Kopie.

jbg 14. Aug 2007 22:30

Re: AsyncCalls - Asynchrone Funktionsaufrufe in Delphi
 
Da ich heute in einen schweren Bug gelaufen bin, der durch einen Bug in TThread.Resume ausgelöst wurde, habe ich die AsyncCalls Unit um einen Workaround aktualisiert.

Der Fehler äußert sich dahingehend, dass es beim Beenden der Anwendung auf ein bereits freigegebenes Thread-Objekt durch TThread.Resume (schreibend) zugegriffen wird.

blackdrake 13. Aug 2008 01:23

Re: AsyncCalls - Asynchrone Funktionsaufrufe in Delphi
 
Wieso nennst du das synchron/asynchron? Wäre seriell/parallel nicht eine viel bessere Ausdrucksweiße?

Seriell ist das bearbeiten von Downloads nach und nach. Und Parallel ist das, was deine "ASync" Methodik macht.

jbg 27. Sep 2008 14:47

Re: AsyncCalls 2.9 - Asynchrone Funktionsaufrufe in Delphi
 
Es gibt nun auch eine neue Version von AsyncCalls 2.9. Diese behebt einige Fehler und nutzt Generics und anonyme Methoden. Das Beispiel auf der ersten Seite habe ich aktualisiert.

Phoenix 27. Sep 2008 15:02

Re: AsyncCalls - Asynchrone Funktionsaufrufe in Delphi
 
Zitat:

Zitat von blackdrake
Wieso nennst du das synchron/asynchron? Wäre seriell/parallel nicht eine viel bessere Ausdrucksweiße?

Naja.. in der Informatik ist es eigentlich so, das man hier tatsächlich von von synchron und asynchron redet. Das ist auch in anderen Sprachen so. Natürlich trifft die Beschreibung mit Seriell & Parallel den Kern der Sache genausogut (wenn nicht sogar besser), aber da sich Synchron/Asynchron inzwischen durchgesetzt hat ist es nur konsequent dabei zu bleiben. Damit weiss ein Informatiker halt, um was es sich hier dreht.

DeddyH 27. Sep 2008 17:04

Re: AsyncCalls 2.9 - Asynchrone Funktionsaufrufe in Delphi
 
Zitat:

Zitat von jbg
Diese behebt einige Fehler und nutzt Generics und anonyme Methoden.

Das heißt, nur mit Delphi 2009 nutzbar?

jbg 27. Sep 2008 18:27

Re: AsyncCalls 2.9 - Asynchrone Funktionsaufrufe in Delphi
 
Zitat:

Zitat von DeddyH
Das heißt, nur mit Delphi 2009 nutzbar?

Ja. Aber der Code is mit einem IFDEF Umgeben, so dass die "alten" Funktionen für Delphi 5 bis 2007 nutzbar bleiben.

jbg 29. Sep 2008 08:53

Re: AsyncCalls 2.9 - Asynchrone Funktionsaufrufe in Delphi
 
Nun hat mich der Delphi 2009 vor den Kopf gestoßen. AsyncCalls 2.9 ist nicht mit Delphi 2009 nutzbar, da bei einem "Make Project" ein interner Compiler Fehler auftritt. Und dass man seine Projekte immer mit "Build Project" erzeugt, halt ich für eine Zumutung.

Assertor 29. Sep 2008 09:03

Re: AsyncCalls 2.9 - Asynchrone Funktionsaufrufe in Delphi
 
Hi,

Zitat:

Zitat von jbg
Nun hat mich der Delphi 2009 vor den Kopf gestoßen. [...] nicht mit Delphi 2009 nutzbar, da [...] ein interner Compiler Fehler auftritt.

Da muß wohl noch an der Stabilität des neuen Compiler gearbeitet werden ;)

Übrigens mal ein großes, dickes

Danke

für Deine gute Arbeit!

Gruß Assertor

jbg 29. Sep 2008 09:19

Re: AsyncCalls 2.9 - Asynchrone Funktionsaufrufe in Delphi
 
[quote="Assertor"]Da muß wohl noch an der Stabilität des neuen Compiler gearbeitet werden ;)
Bei der neuen Version 2.91, die ab jetzt verfügbar ist, habe ich sämtliche Generics auskommentiert. Ich hoffe, dass das "Delphi 2009 Update 1" bald kommt, da Generics so für mich nicht wirklich nutzbar sind. Da ich nicht gerne russisches Roulette spiele.

Zitat:

Übrigens mal ein großes, dickes Danke
Danke.

Assertor 2. Okt 2008 14:25

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Hi,

so und um jetzt mal dem theoretischen Teil der Forschung und Entwicklung etwas mehr Breite zu verpassen, hier eine Thread Library für Delphi von Primoz, dem Autoren von OmniXML:

OmniThreadLibrary

Gruß Assertor

BloodySmartie 1. Dez 2008 16:13

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Ich bin mir noch nicht ganz sicher ob es nicht doch an mir liegt, aber ich glaube ich habe einen Bug gefunden.

In der Execute-Prozedur eines Threads passiert u.a. folgendes:
Delphi-Quellcode:
  c:=qry.RecordCount;
  setlength(self.Machines,c);
  qry.First;

  for I := 0 to c - 1 do begin
    tmps:=utf8toansi(qry.FieldByName('phonenumber').AsString);
    p:=localFindMachine(tmps);
    EnterMainThread;
    try
      p^.isDeleted:=true;
      p^.isactive:=false;
    finally
      LeaveMainThread;
    end;
    qry.Next;
  end;
Soweit ich das nun verfolgen konnte, hat c zunächst den erwarteten Wert - nämlich die Zahl der Datensätze aus meiner SQL-Abfrage. Nach LeaveMainThread hat c aber den Wert 0.

Vielleicht habe ich aber auch die Funktionsweise von EnterMainThread / LeaveMainThread falsch verstanden.

jbg 1. Dez 2008 16:50

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Wo hast du c denn deklariert?
Wird noch von woanders darauf zu gegriffen?
"for I := 0 to c - 1 do begin" Der Compiler braucht c nach dieser Zeile nicht mehr, da die Schleife rückwärts laufen kann. Der Debugger weiß das aber nicht und zeigt möglicherweise den Inhalt an, der zufälligerweise im CPU Register steht, das vorher für c benutzt wurde.

BloodySmartie 1. Dez 2008 16:59

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
c ist lokal deklariert und ich greife weiter unten nochmal in einer Verzweigung darauf zu, in der ich Prüfe, ob c größer als 0 ist. Das schlug dauernd fehl, so bin ich auf das Problem gestoßen.

sirius 1. Dez 2008 17:02

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Dein Compiler dürfte dir eine Warnung ausgeben: "c nach Schleifendurchlauf undefiniert" oder so ähnlich

BloodySmartie 1. Dez 2008 17:06

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Eine solche Warnung bekomme ich auch nicht. Das verstehe ich jetzt sowieso nicht. Warum sollte mein Compiler denn c einfach wegwerfen, nur weil ich es in der Schleife verwende? besonders, wo es ein paar Zeilen weiter unten ja noch gebraucht wird. Das habe ich auch vorher noch nie beobachtet.

sirius 1. Dez 2008 17:12

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Er wirft c ja nicht weg, er hat es eigentlich nur nie gehabt. Er benutzt es nur in den Registern.

jbg 1. Dez 2008 17:13

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Schalte mal die Stackframes für die Funktion an. Einfach vor die Methode ein {$STACKFRAMES ON} und danach ein {$STACKFRAMES OFF}.

BloodySmartie 2. Dez 2008 08:17

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Mit Stackframes bekomme ich nun in dem weiter unten gelegenen Codeabschnitt beim Zugriff auf den Hauptthread eine Schutzverletzung:
Delphi-Quellcode:
 if c>0 then begin
    EnterMainThread;
    try
      log(inttostr(c)+' Automat(en) von Mandant '+clientData.cname+' gelöscht, Laufzeit '+inttostr(et-st)+' ms ',1);
      dispatcherForm.deletedMachinesLED.Status:=false; //   <---- Schutzverletzung tritt hier auf
      dispatcherform.lDeletedMachineLED.Caption:='Suche nach gelöschten Automaten (letzte Laufzeit: '+inttostr(et-st)+' ms )';
    finally
      LeaveMainThread;
    end;

  end;
Nachtrag:

Die Schutzverletzung tritt in Unit System auf, in Methode TObject.InheritsFrom

BloodySmartie 5. Feb 2009 16:23

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Zwar konnte ich meinen Fehler aus dem letzten Post nicht mehr wirklich ausfindig machen aber ich habe inzwischen noch etwas gefunden, das mich stutzig macht. Meine Anwendung, die zwei oder mehr asynchrone Aufrufe derselben Prozedur gleichzeitig laufen lässt, hängt sich auf, sobald in der Prozedur ein exit aufgerufen wird. Das passiert übrigens nicht sofort, sondern vielmehr nach 50-100 Aufrufen.

In der Unit asynccalls, wie auch hier, konnte ich nur von Problemen mit Exit innerhalb eines EnterMainThread-LeaveMainThread - Blocks lesen, das aber auch gelöst zu sein scheint. Um nicht wieder als doof abgestempelt zu werden: Was ist mein Fehler? Ich würde mich wirklich freuen, wenn mir das jemand erklären würde.

jbg 5. Feb 2009 17:31

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Da ich deinen Code nicht kenne, kann ich nur Rätseln woran das liegen kann. Das kann von Speicherüberschreibungen deines Codes bis zu glattem Fehlverhalten meines Codes reichen.

Zitat:

Zitat von BloodySmartie
Meine Anwendung, die zwei oder mehr asynchrone Aufrufe derselben Prozedur gleichzeitig laufen lässt

Ich hoffe, dass du nicht annimmst, dass AsyncCall gemeinsame Variable schützt.

Ansonsten würde ich dir zu den TAsyncCalls.Invoke() bzw. TAsyncCalls.VCLSync() / TAsyncCalls.VCLInvoke() Methoden raten, denn die basieren nicht auf Annahmen über den kompilierten Code. Wahrscheinlich generiert der Compiler bei dir einen Code der von den alten AsyncCalls Methoden nicht behandelt wird/werden kann.

Delphi-Quellcode:
EnterMainThread;
try
  bla;
finally
  LeaveMainThread;
end;
Wir mit den neuen Methoden zu:
Delphi-Quellcode:
TAsyncCalls.VCLSync(procedure
  begin
    bla;
  end);

BloodySmartie 6. Feb 2009 00:08

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Das Ganze lässt sich in wenigen Zeilen reproduzieren. Ich lade morgen mal ein kleines Testprojekt hoch, sobald ich auf Arbeit bin.

Beste Grüße,

David

MStoll 16. Mär 2009 19:42

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Hallo,

ich möchte dem Autor für diese Arbeit ein großes Lob aussprechen. Wirklich ein sehr nützliches und gut funktionierendes Tool.
Dafür vielen Dank!

Gruß
Michael

Dezipaitor 17. Mär 2009 21:31

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Warum hört die VCL auf, auf Eingaben zu reagieren, wenn ich Invoke oder AsynCall aufrufe? Ich dachte, das geht alles über Threads? Die Funktion springt ja auch sofort zurück, aber eine Form reagiert nicht mehr.

Was man immer machen muss ist:
Delphi-Quellcode:
while AsyncMultiSync([x], True, 10) = WAIT_TIMEOUT do
  Application.ProcessMessages;
Aber muss das wirklich so sein?

Dezipaitor 17. Mär 2009 21:36

Re: AsyncCalls 2.91 - Asynchrone Funktionsaufrufe in Delphi
 
Zitat:

Zitat von jbg
Da es sich um ein Interface handelt, wird beim Verlassen der Funktion/Methode auf alle noch ausstehenden asynchronen Funktionen gewartet.

Ha, selbst beantwortet. :bouncing4:


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:38 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