exilon / QuickLib

Quick development library (AutoMapper, LinQ, IOC Dependency Injection, MemoryCache, Scheduled tasks, Json and Yml Config and Options pattern, Serializers, etc) with crossplatform support for Delphi/Firemonkey (Windows,Linux,OSX/IOS/Android) and freepascal (Windows/Linux).
Apache License 2.0
623 stars 186 forks source link

TAnonymousThread.Execute_Sync question.. #134

Open ijbranch opened 1 week ago

ijbranch commented 1 week ago

D12.2, 32 bit App. I have implemented this code:

TAnonymousThread.Execute_Sync(procedure
      begin
        try
          //
          var dtStart: TDateTime := Now;
          //
          DBSBackup.Execute('BACKUP DATABASE "DBiArchive" AS "DBiArchive Manual Backup - ' + FormatDateTime('yyyy-mm-dd @ HHnn',
            now) + '" TO STORE "Backups" COMPRESSION 9 INCLUDE CATALOG');
          //
          BackupsLog.Open;
          BackupsLog.AppendRecord([dtStart, now, 'DBiArchive', 'Manual', AUD.UserID, now]);
          BackupsLog.Close;
          //
        except
          // Handle any exceptions that might occur during the execution of the SQL code
          on E: Exception do ShowMessage('Error performing database backup: ' + E.Message);
          //
        end;
      end
      )
      .Start;

I interpreted Execute_Sync would allow me to move the Form while the thread executed. It would seem not. How to be able to please?

Regards & TIA,Ian

exilon commented 4 days ago

Use Execute_Sync only if inside code updates UI like a button, label, etc. In your case seems you're not updating any UI interface.

ijbranch commented 4 days ago

Ahhh. Ok. Tks. So, using just "TAnonymousThread.Execute(procedure..." then, how do I keep the UI responsive so the form can be moved? Or do I use one of the other Quick.Threads options?

exilon commented 4 days ago

Sorry, Showmessage is UI dialog, you need Execute_Sync() Where you're calling this? There're any code after this call?

ijbranch commented 4 days ago

This is the complete function:

function TMainForm.ARBackup: Boolean;
begin
  //
  try
    //
    nlhWaiter2.Active := True;
    //
    // Create an anonymous thread to execute the SQL code
  TAnonymousThread.Execute_Sync(procedure
      begin
        try
          //
          var dtStart: TDateTime := Now;
          //
          DBSBackup.Execute('BACKUP DATABASE "DBiArchive" AS "DBiArchive Manual Backup - ' + FormatDateTime('yyyy-mm-dd @ HHnn',
            now) + '" TO STORE "Backups" COMPRESSION 9 INCLUDE CATALOG');
          //
          BackupsLog.Open;
          BackupsLog.AppendRecord([dtStart, now, 'DBiArchive', 'Manual', AUD.UserID, now]);
          BackupsLog.Close;
          //
        except
          // Handle any exceptions that might occur during the execution of the SQL code
          on E: Exception do ShowMessage('Error performing database backup: ' + E.Message);
          //
        end;
      end
      )
      .Start;
    //
    Result := True;
    //
    nlhWaiter2.Active := False;
    //
  except
    on E: Exception do
    begin
      //
      Screen.Cursor := crDefault;
      if (E is EDatabaseError) and (E is EEDBError) then
        StyledTaskMessageDlg('Unhandled Database error!', 'An Unexpected database error # ' + EEDBError(E).ErrorCode.ToString + ' occurred!' +
          sCRLF + EEDBError(E).ErrorMsg + sCRLF + 'Please inform your Workflow System Administrator.', mtError, [mbOK], 0)
      else
        StyledTaskMessageDlg('Unhandled error!', 'An unexpected error has occurred while doing the Backup!' + sCRLF + E.Message + sCRLF +
          'Please inform your Workflow System Administrator.', mtError, [mbOK], 0);
      //
      btnClose.Enabled := True;
      btnRestore.Enabled := True;
      btnBackup.Enabled := True;
      //
      Backup.Refresh;
      Backup.Last;
      //
      Screen.Cursor := crDefault;
      //
      lBackupDone := False;
      //
      Result := False;
      //
    end;
    //
  end;
  //
end;
exilon commented 4 days ago

Try something like this:

function TMainForm.ARBackup: Boolean;
begin
    // Create an anonymous thread to execute the SQL code
  TAnonymousThread.Execute_Sync(procedure
      begin
        nlhWaiter2.Active := True;
          //
          var dtStart: TDateTime := Now;
          //
          DBSBackup.Execute('BACKUP DATABASE "DBiArchive" AS "DBiArchive Manual Backup - ' + FormatDateTime('yyyy-mm-dd @ HHnn',
            now) + '" TO STORE "Backups" COMPRESSION 9 INCLUDE CATALOG');
          //
          BackupsLog.Open;
          BackupsLog.AppendRecord([dtStart, now, 'DBiArchive', 'Manual', AUD.UserID, now]);
          BackupsLog.Close;
          Result := True;
      end;
      )
      .OnException(procedure(aException : Exception)
      begin
        //
        ShowMessage('Error performing database backup: ' + aException.Message);
        Screen.Cursor := crDefault;
        if (E is EDatabaseError) and (E is EEDBError) then
          StyledTaskMessageDlg('Unhandled Database error!', 'An Unexpected database error # ' + EEDBError(E).ErrorCode.ToString + ' occurred!' +
            sCRLF + EEDBError(E).ErrorMsg + sCRLF + 'Please inform your Workflow System Administrator.', mtError, [mbOK], 0)
        else
          StyledTaskMessageDlg('Unhandled error!', 'An unexpected error has occurred while doing the Backup!' + sCRLF + E.Message + sCRLF +
            'Please inform your Workflow System Administrator.', mtError, [mbOK], 0);
        //
        btnClose.Enabled := True;
        btnRestore.Enabled := True;
        btnBackup.Enabled := True;
        //
        Backup.Refresh;
        Backup.Last;
        //
        Screen.Cursor := crDefault;
        //
        lBackupDone := False;
        //
        Result := False;
      //
     end)
    .OnTerminate_Sync(procedure
      begin
        nlhWaiter2.Active := False;
      end;)
    .Start;
end;
ijbranch commented 3 days ago

Thank you.
Unfortunately Result does not carry into the Thread. Nor it seems does E for the Exception.

image

exilon commented 3 days ago

Sorry, I did without an Delphi IDE. You need to change E variable with aException procedure parameter inside OnException. Result is not needed because you have to change to procedure. No senses to have a function than launches an async method without waiting for them and get a result. After .start, delphi continues with execution, but TAnonymousThread opens a separate execution thread.