Reputation: 583
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
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