0

My primary goal is to run two time consuming functions or procedures one after the other has finished executing. My current approach is to place the second function invocation after the while loop (assuming I have passed one Interface type object to it in the AsyncMultiSync array param) in the code below I got from AsyncCalls Documentation.md in Github. Additionally, when I am trying to run the exact provided code below, I see that the threads do their job and the execution reaches to the first access to the vcl thread Memo but the second access to the memo freezes the application (for directories having quite a lot of files in the GetFiles call) P.S. English is not my first language and I might have trouble explaining it but if you demote this for title or MCVE, it will be my last question here as per SO rules.

uses
  ..AsyncCalls;

{ Ex - 2 using global function }
function GetFiles(Directory: string; Filenames: TStrings): Integer;
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);
    Winapi.Windows.FindClose(h);
  end;
  Result := 0;
end;

procedure TForm1.ButtonGetFilesClick(Sender: TObject);
var
  i: integer;
  Dir1Files, Dir2Files: TStrings;
  Dir1, Dir2 IAsyncCall;
begin
  Dir1Files := TStringList.Create;
  Dir2Files := TStringList.Create;
  ButtonGetFiles.Enabled := False;
  try
    Dir1 := TAsyncCalls.Invoke<string, TStrings>(GetFiles, 'C:\portables\autoit-v3', Dir1Files);
    Dir2 := TAsyncCalls.Invoke<string, TStrings>(GetFiles, 'E:\mySyntax-Repository-works', Dir2Files);    


    { Wait until both async functions have finished their work. While waiting make the UI
      reacting on user interaction. }
    while AsyncMultiSync([Dir1, Dir2], True, 10) = WAIT_TIMEOUT do
      Application.ProcessMessages;
{    Form1.Caption := 'after file search';}

    MemoFiles.Lines.Assign(Dir1Files);
    MemoFiles.Lines.AddStrings(Dir2Files); {-->causes freeze}
  finally
    ButtonGetFiles.Enabled := True;
    Dir2Files.Free;
    Dir1Files.Free;
  end;
end;
user30478
  • 347
  • 1
  • 3
  • 13
  • Not my DV, but this demo is pretty pointless. It's starting two threads which are competing for I/O and then spinning `ProcessMessages` while waiting for them to complete. This is just needlessly complicated and isn't gaining you anything. As for the freeze - does it eventually unfreeze and update the screen? Or does it stay frozen forever? How many items are in `Dir2Files`? Does the behaviour change if you use a different target directory (one with fewer files?). – J... Nov 10 '18 at 10:13
  • @J... execution freezes forever, I am terminating it improperly. With less number of files no problem arises giving me the impression that I am making the demo single threaded instead of multithreaded as the library promises. According to the docs, the given AsyncMultiSync while loop should break once all the tasks have returned. And once we have both the results there shouldn't be any race condition. Which is what I want to exploit for a function execution after another function execution (not in the code above). I see that instead of Memo if I access a common stringlist there is no problem. – user30478 Nov 10 '18 at 14:29
  • 1
    What happens when you pause execution when it's frozen? Did you try debugging? I suspect you may have a directory with a very large number of files and you are simply running out of patience when deciding it is "frozen". `Memo.Lines.Assign` and `Memo.Lines.AddStrings` can be extremely slow with large updates if you don't `.BeginUpdate` and `.EndUpdate` around them. – J... Nov 11 '18 at 12:50
  • @J.. You're right. after wrapping with `.BeginUpdate` `.EndUpdate` it took around 7-8 minutes while the cursor was just an I-beam. If I had clicked on the form the title-bar would say 'not responding' with an 'Windows is finding a solution to the problem' modal dialog. So the bottleneck was the memo update part which was the root of the single threaded behavior and there is no race condition for the memo as expected after `AsyncMultiSync`. – user30478 Nov 12 '18 at 05:38
  • @J.. Can you just post the fixed code as an answer or do whatever can be done of this question. Since the code in the docs is not foolproof, this may improve it for this great library. I am glad I found two solutions. – user30478 Nov 12 '18 at 05:42
  • 1
    Possible duplicate of [TMemo is painfuly slow when working with large number of lines](https://stackoverflow.com/questions/46990961/tmemo-is-painfuly-slow-when-working-with-large-number-of-lines) – J... Nov 12 '18 at 13:40
  • 1
    Since we've now found the actualy problem (nothing to do with AsyncCalls, everything about `TMemo` having poor performance), I've instead suggested a duplicate. – J... Nov 12 '18 at 13:41

1 Answers1

0

One alternative solution to use is JvThread as it contains well commented demos. Multiple JvThread objects can be wired via onFinish events to start one after another. If required, that many Sync functions can be constructed to talk to the VCL thread where race risk exists(between the thread and the VCL thread). And if required, each JvThread can be force-finished i.e.'breaked' by terminating it, based on some logic, inside of the thread execution code or in the associated Sync function in the VCL thread. How is it different from using timers or threaded timers triggering each other one after another in the first place given we use quite a few global form fields? Answer is there is no onFinish equivalent for timers and it will take more effort and less elegance to achieve the same. Omnithread is somewhat restrictive for its BSD licence, Native threads beat the RAD spirit of Delphi, and Task library not available in lighter installs like XE5.

user30478
  • 347
  • 1
  • 3
  • 13