Wait for Thread to finish to proceed with the code - Delphi

Asked

Viewed 3,721 times

2

I’m facing a problem in an application that I developed. It is an automatic updater that basically downloads the necessary files and extracts them in a suitable path. The problem is time to download. I put the download method inside a Thread, I put it inside Syncronize, but it "hangs", does not let me move the window(interface) until the process is over. Remembering that when downloading it calls the method that changes the progress bar.

In another case, I put only the method that changes the loading bar inside the syncronize, making the window no longer locked, I can move it while the download is done, but all the rest of the code runs and this can not happen in my case, because I depend on the file being downloaded to continue.

The final question is, how do I wait for this download to end, this Thread to finish, before proceeding with the execution of the code? Because if I proceed it will give error in the application for not having the file, as it is still being downloaded.

Call the download method within the main form (uFrmPrincipal.pas)

ThreadBaixarAtualização('c:\caminho\atua_beta',
  'http://www.site.com.br/atualizacao/atua_beta.zip', true);

ThreadBaixarAtualização('c:\caminho\scripts',
    'http://www.site.com.br/atualizacao/scripts.zip', False);

Method to create the Thread instance within the Main Form (uFrmPrincipal.pas)

procedure TfrmAtualizador.ThreadBaixarAtualização(CaminhoNomeArquivo,
    URL: String; Status: Boolean);
  begin
    FConHTTPThread := TConHTTPThread.create(true, UpdateProgressBar);
    FConHTTPThread.FreeOnTerminate := true;
    FConHTTPThread.URL := URL;
    FConHTTPThread.CaminhoNomeArquivo := CaminhoNomeArquivo;
    FConHTTPThread.Status := Status;
    FConHTTPThread.Start;
  end;

Method that changes the progress bar in the main form (uFrmPrincipal.pas)

  procedure TfrmAtualizador.UpdateProgressBar(aProgress, Total: Int64);
  begin
    gProgresso.MaxValue := Total;
    gProgresso.Progress := aProgress;
    gProgresso.Update;
  end;

Class(Unit) to download the files - Separate Unit(uConHTTPThread.pas)

  unit uConHTTPThread;

  interface

  uses
    System.Classes, SysUtils, Forms, IdHTTP, IdComponent,
    Vcl.Dialogs, System.UITypes;

  type
    // Tipo que contem um método com os mesmos parâmetros que a barra de progresso
    // do formulario principal
    TBarraProgress = procedure(aProgress, Total: Int64) of object;

    TConHTTPThread = class(TThread)
      procedure IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode;
        AWorkCount: Int64);
      procedure IdHTTP1WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
        AWorkCountMax: Int64);
      procedure IdHTTP1WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
    private
      FIdHTTP1: TIdHTTP;
      FValorMaximo: Int64;
      FProgresso: Int64;
      FfileDownload: TFileStream;
      FCaminhoNomeArquivo, FURL: String;
      FStatus: Boolean;
      FBarraProgress: TBarraProgress;
      procedure SetCaminhoNomeArquivo(const Value: String);
      procedure SetURL(const Value: String);
      procedure SetStatus(const Value: Boolean);
      procedure AtualizaBarra;
    protected
      procedure Execute; override;
      procedure BaixarAtualizacao;
      procedure Progress(aProgress: Int64); virtual;
    Public
      property Progresso: Int64 read FProgresso;
      property Total: Int64 read FValorMaximo;
      property CaminhoNomeArquivo: String read FCaminhoNomeArquivo
        write SetCaminhoNomeArquivo;
      property URL: String read FURL write SetURL;
      property Status: Boolean read FStatus write SetStatus;

      constructor Create(CreateSuspended: Boolean;
        pBarraProgress: TBarraProgress); reintroduce; virtual;
    end;

  implementation

  // Responsável por efetuar o Donwload dos arquivos de atualização
  procedure TConHTTPThread.BaixarAtualizacao;
  begin
    try
      if not Assigned(FIdHTTP1) then
        FIdHTTP1 := TIdHTTP.Create(Nil);

      FfileDownload := TFileStream.Create(FCaminhoNomeArquivo +
        ExtractFileExt(FURL), fmCreate);

      if Status = True then
      begin
        with FIdHTTP1 do
        begin
          OnWork := IdHTTP1Work;
          OnWorkBegin := IdHTTP1WorkBegin;
        end;
      end;
      FIdHTTP1.Get(FURL, FfileDownload);
    finally
      if Assigned(FIdHTTP1) then
        FreeAndNil(FIdHTTP1);
      if Assigned(FfileDownload) then
        FreeAndNil(FfileDownload);
    end;
  end;

  constructor TConHTTPThread.Create(CreateSuspended: Boolean;
    pBarraProgress: TBarraProgress);
  begin
    inherited Create(CreateSuspended);
    FBarraProgress := pBarraProgress;
  end;

  procedure TConHTTPThread.Execute;
  begin
    if (not Terminated) then
    begin
      Sleep(10);
      Synchronize(BaixarAtualizacao);
    end;
  end;

  procedure TConHTTPThread.IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode;
    AWorkCount: Int64);
  begin
    Progress(AWorkCount);
  end;

  procedure TConHTTPThread.IdHTTP1WorkBegin(ASender: TObject;
    AWorkMode: TWorkMode; AWorkCountMax: Int64);
  begin
    // Verifica tamanho total do arquivo a ser baixado
    FValorMaximo := AWorkCountMax;
  end;

  procedure TConHTTPThread.IdHTTP1WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
  begin
    FValorMaximo := FValorMaximo;
  end;

  procedure TConHTTPThread.Progress(aProgress: Int64);
  begin
    FProgresso := aProgress;
    AtualizaBarra;
  end;

  procedure TConHTTPThread.SetCaminhoNomeArquivo(const Value: String);
  begin
    FCaminhoNomeArquivo := Value;
  end;

  procedure TConHTTPThread.SetStatus(const Value: Boolean);
  begin
    FStatus := Value;
  end;

  procedure TConHTTPThread.SetURL(const Value: String);
  begin
    FURL := Value;
  end;

  procedure TConHTTPThread.AtualizaBarra;
  begin
    // Atualiza barra de progresso no formulário principal
    if Assigned(FBarraProgress) then
      FBarraProgress(FProgresso, FValorMaximo);
  end;

  end.

3 answers

1


If you use Synchronize there inside the thread Execute, who will perform the work of executing the code block "Download" will be the main thread, ie in this case you kind of "killed" the use of the thread and made your program run the download process without it.

For your need, you can implement a method that is executed when finishing the thread, and arrow it in the thread’s Onterminate when creating it, follow a simple example that I did, when the thread finishes, will run the code block that is inside Finalizarthread.

TTeste = class(TThread)
public
    procedure Execute;
    procedure FinalizarThread(Sender: TObject);

    constructor Create;
end;

implementation 

{$R *.dfm}

{ TTeste }

constructor TTeste.Create;
begin
    OnTerminate :=FinalizarThread;
end

procedure TTeste.Execute;
begin
    ////Executa o processo de download
end;

procedure TTeste.FinalizarThread(Sender: TObject);
begin
    //Processo a realizar após finalizar a thread
end;
  • I get it. I’m going to work on that idea. Thank you.

  • Okay, anything I’m up for.

1

The correct is to even use Syncronize only to update the graphical interface (not to download). I’ve done something similar using Thread’s Onterminate event: In one variable it stored the total number of threads and in Onterminate Handler it decremented the variable. At zero, the processing continued.

0

Just like you have a trial called IdHTTP1WorkBegin, you can develop a IdHTTP1WorkEnd, whereas in the past IdHTTP1WorkBegin you can put some features (Controls like buttons and etc) with the Enabled = False and in the IdHTTP1WorkEnd rehabilitate these disabled controls.

The goal of the thread is precisely not to have Mainthread wait for the completion of the secondary process, so what can be done is an auxiliary control as suggested above.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.