JRG
JRG

Reputation: 583

Anonymous Thread procedure using var parameter

This is not the first time I face an issue similar to this one : how can I get a var parameter returning values from whithin a AnonymousThread (or a TTask) ?

I need to run a procedure that takes time and consume high CPU, along the running it will populate a record with LOG information of processing that will be used later in another modules. Once there were other process to run at the same time, I put this procedure in an AnoynymousThread. Unfortunately, when it finished, I could not use the record data of LOG because it was empty.

What should I do to get this record been populated during processing inside the Thread ?

See my code : [UNIT ]

unit UntStringThread;

interface    
uses
   System.Classes,
   System.SysUtils,
   System.Threading;

   TYPE
    TRCLog = record
       logprocess : Tstringlist;
       logrecord  : Array of Tstringlist;
    end;    

    Procedure DoBigProcess (var pRecLog : TRClog; piterations : integer);
    Procedure MajorProcessing (var pmainLog : TRCLog);

implementation 

   Procedure DoBigProcess (var pRecLog : TRClog; piterations : integer);
   Var
       localLog                 : TRCLog;
       lrows,lrecs, ltotalrecs  : integer;
       strlogrecords            : string;

   begin
        //Define pRecLog.logrecord array size
        setlength(precLog.logrecord, piterations);

        // Starts the piterations of the main process which will generates a LOG
        for lrows := 1 to piterations do
        begin
              // ... here goes critial processing that generates

              // Generate Log for the current iteration
              pRecLog.logprocess.Add('Iteration : ' + lrows.Tostring + ' ... log data about current iteration ...');

              // ... other processing goes here. Suppose total records is 10
              ltotalrecs := 10;


              // Generate Log for each record
              pRecLog.logrecord[lrows-1] := Tstringlist.Create;
              for lrecs := 1 to ltotalrecs do
              begin
                     //  some additionl processing here that generates detail log of records being processed
                     strlogrecords := '    Record : ' + lrecs.tostring + '... log data about current record being processed';
                     pRecLog.logrecord[lrows-1].Add(strlogrecords);
              end;
              pRecLog.logrecord[lrows-1].Add(' ');
         end;
   end;


   Procedure MajorProcessing (var pmainLog : TRCLog);
   var
       llog       : TRClog;
       litems     : integer;

   begin
        // some initialization processing here
        // ...
        litems := 10;

        // Runs BigProcess in an Anonymous  Thread
        llog := pmainlog;
        TThread.CreateAnonymousThread(procedure
                                      begin
                                            DoBigProcess (lLog, litems);
                                      end).Start;

        // Continuing process other stuff here

   end;
end.

Main Program

program ProjThreadParameter;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  System.Classes,
  UntStringThread in 'UntStringThread.pas';
var
     idxlog, idxrecord: integer;
     logmain : TRClog;
begin
  try
      logmain.logprocess := Tstringlist.Create;
      MajorProcessing (logmain);

      {==> This call, without Thread, works fine}
      //DoBigProcess(logmain,10);

      for idxlog :=0  to logmain.logprocess.Count-1 do
      begin
           Writeln(logmain.logprocess[idxlog]);
           for idxrecord :=0 to logmain.logrecord[idxlog].count-1 do
               Writeln(logmain.logrecord[idxlog][idxrecord])
      end;
      Readln;

      // PROBLEM ==> when using Thread logmain record returned empty !!

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

I had studied a previous question I've made some years ago, but I could not make it work this time. iTask - values parameters to anonymous procedure

Appreciatte your help!

Upvotes: 0

Views: 599

Answers (1)

Paul McGee
Paul McGee

Reputation: 104

This question is similar to How do I pass a context inside a loop into a TTask.IFuture in Delphi?

IFuture is the concurrent function to accompany ITask concurrent procedures.

If we create a function variable that depends on the loop index, then we can bind the value of the index into the function to be used as a Future.

This version creates an array of strings. The same will work for your TRCLog record.

program ProjThreadLogging;
{$APPTYPE CONSOLE}
uses
     System.SysUtils,
     System.Classes,
     System.Threading;

const
     procs = 8;

var
     idxlog     : integer;
     s          : string;
     futurefunc : TFunc<string>;
     logstrs    : array [1..procs] of IFuture<string>;

function CreateIdxLogFunc(idxlog:integer):TFunc<string>;
     begin
       Result := function:string
                 begin
                    {Some calculation that takes time}
                    Sleep(1000*idxlog);
                    Result:='Proc ' + idxlog.ToString;
                 end;
     end;

begin
     for idxlog in [1..procs] do begin
         futurefunc      :=   CreateIdxLogFunc(idxlog);
         logstrs[idxlog] :=   TTask.Future<string>( futurefunc );
     end;

     for idxlog in [1..procs] do begin
         writeln(logstrs[idxlog].Value);
     end;

     Readln;
end.  

Upvotes: 1

Related Questions