Search code examples
multithreadingdelphiparallel-processingsynchronize

How to synchronize TParallell in Delphi XE7 to log data


I need to log some data and preferrably copy files using a thread, but by using the code below, but it simply freezes my app.

if I understand the whole XE7 Parallell library correctly TThread.Queue and TThread.Synchronize are supposed to sync with the main thread, but in my case the whole app freezes.

What am I doing wrong?

procedure TCopyDeviceContent.StartCopy;
var
  OK: boolean;
begin
  OK := false;

  //  showmessage('fFiles.Count = '+inttostr(fFiles.Count));

  if fFiles.Count = 0 then
  begin
    NotifyDone;
    exit;
  end;

  TParallel.For(0, fFiles.Count-1,
    procedure (Value: Integer)
    begin
      TThread.Queue(TThread.CurrentThread, //Here is where it freezes
        procedure
        begin
          Log('Setting fCurrentFile to '+fFiles.Strings[value]);
        end
      );

      sleep(1000);

      fCurrentFile := fFiles.Strings[value];
      Log('Triggering fOnBeforeProcess');
      if assigned(fOnBeforeProcess) then fOnBeforeProcess(self);

      Log('Does file exist?');
      if FileExists(fFiles.Strings[value]) = true then
      begin
        Log('Yes!');
        Log('Trying to copy file to Windows temp folder.');
        try
          TFile.Copy(fFiles.Strings[value], GetEnvironmentVariable('TEMP'));
        finally
          OK := true;
        end;

        if OK = true then
        begin
          Log('Success!');
          OK := false;

          Log('Does file exist in Windows temp folder?');
          if FileExists(GetEnvironmentVariable('TEMP')+ExtractFileName(fFiles.Strings[value])) then
          begin
            Log('Yes');
            Log('Trying to copy file from Windows temp folder to final destination: '+DestPath+DateToStr(Now)+'\'+ExtractFileName(fFiles.Strings[value]));
            try
              TFile.Move(GetEnvironmentVariable('TEMP')+ExtractFileName(fFiles.Strings[value]),
              DestPath+DateToStr(Now)+'\'+ExtractFileName(fFiles.Strings[value]));
            finally
              fFilesOK.Add(fFiles.Strings[value]);
              Log('Sucess!');
            end;
          end;    
        end
        else
        begin
          fFilesFailed.Add(fFiles.Strings[value]);
          Log('Failed copying to Windows temp folder!');
        end;
      end;
      inc(fProgress);
      NotifyProgress;
      Log('File copy success. Moving on to next file if available...');
    end
  );

  NotifyDone;

  if fFilesFailed.Count > 0 then NotifyError;
end;

Solution

  • If goal is just to copy files without freezing UI thread i would just use something like this:

    procedure TCopyDeviceContent.StartCopy;
    var
     aTask: ITask;
    begin
     aTask := TTask.Create (procedure ()
       begin
          // Copy files here  
          TThread.Synchronize(nil,procedure
                      begin
                         //Interact with UI  
                         Form1.Memo1.Lines.Add(‘Begin Execution’);
                      end);
       end);
     aTask.Start;
    end;
    

    Inside task procedure just copy files as you would do it usually, i am not sure if copying using multiple threads will help you.

    In case you need interact with UI you need to switch back to UI thread, you can use TThread.Synchronize.