In Delphi 10.1 Berlin I would like to add the possibility to stop the responsive TParallel.&For loop from my question How to make a TParallel.&For loop responsive and store values in a TList<T>? .
The loop calculates values and stores these values in a TList. It runs in a separate thread with TTask.Run to make it responsive:
type
TCalculationProject=class(TObject)
private
Task: ITask;
...
public
List: TList<Real>;
...
end;
procedure TCalculationProject.CancelButtonClicked;
begin
if Assigned(Task) then
begin
Task.Cancel;
end;
end;
function TCalculationProject.CalculateListItem(const AIndex: Integer): Real;
begin
//a function which takes a lot of calculation time
//however in this example we simulate the calculation time and
//use a simple alogorithm to verify the list afterwards
Sleep(30);
Result:=10*AIndex;
end;
procedure TCalculationProject.CalculateList;
begin
List.Clear;
if Assigned(Task) then
begin
Task.Cancel;
end;
Task:=TTask.Run(
procedure
var
LoopResult: TParallel.TLoopResult;
Lock: TCriticalSection;
begin
Lock:=TCriticalSection.Create;
try
LoopResult:=TParallel.&For(0, 1000-1,
procedure(AIndex: Integer; LoopState: TParallel.TLoopState)
var
Res: Real;
begin
if (Task.Status=TTaskStatus.Canceled) and not(LoopState.Stopped) then
begin
LoopState.Stop;
end;
if LoopState.Stopped then
begin
Exit;
end;
Res:=CalculateListItem(AIndex);
Lock.Enter;
try
List.Add(Res);
finally
Lock.Leave;
end;
end
);
finally
Lock.Free;
end;
if (Task.Status=TTaskStatus.Canceled) then
begin
TThread.Synchronize(TThread.Current,
procedure
begin
List.Clear;
end
);
end
else
begin
if LoopResult.Completed then
begin
TThread.Synchronize(TThread.Current,
procedure
begin
SortList;
ShowList;
end
);
end;
end;
end
);
end;
The current running calculation Task should be stopped when
- the calculation is restarted
- the user click on a cancel button
I added
if Assigned(Task) then
begin
Task.Cancel;
end;
at the beginning of procedure TCalculationProject.CalculateList and in procedure TCalculationProject.CancelButtonClicked which is called when the cancel button is clicked.
The loop is stopped with
if (Task.Status=TTaskStatus.Canceled) and not(LoopState.Stopped) then
begin
LoopState.Stop;
end;
if LoopState.Stopped then
begin
Exit;
end;
and the list is cleared with
if (Task.Status=TTaskStatus.Canceled) then
begin
TThread.Synchronize(TThread.Current,
procedure
begin
List.Clear;
end
);
end
This does not work when I restart the calculation. Then two calculation tasks are running. I tried to add a Task.Wait after Task.Cancel to wait for the task to be finished before we start the new calculation but without success.
What is the correct fully thread-safe way to implement such a cancel/stop function?