I am trying to implement multithreading in Delphi 10.4 (Sydney). I am using TTask to run tasks concurrently and TThreadPool to limit the simultaneously running threads.
Here's my code:
var
MyThreadPool: TThreadPool;
I: Integer;
Tasks: TArray<ITask>;
function ProcessTask(const TaskId: Integer): TProc;
begin
Result := procedure
Begin
WriteLn(Format('Started TaskId: %d ThreadId: %d',[TaskId,TThread.Current.ThreadID]));
Sleep(2000); // Simulate work
WriteLn(format('TaskId %d completed. ThreadId: %d',[TaskId, TThread.Current.ThreadID]));
End;
end;
begin
try
Writeln('Creating TThreadPool');
MyThreadPool := TThreadPool.Create;
try
MyThreadPool.SetMinWorkerThreads(1);
MyThreadPool.SetMaxWorkerThreads(4);
// Hold 10 tasks
SetLength(Tasks,10);
for I := 0 to High(Tasks) do
Begin
Tasks[I] := TTask.Create(
ProcessTask(I+1),
MyThreadPool
);
// Start the task
Tasks[I].Start;
End;
// Wait for all tasks to complete
for I := 0 to High(Tasks) do
Tasks[I].Wait;
WriteLn('All tasks completed.');
ReadLn;
finally
MyThreadPool.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
The output of the above code is:
Creating TThreadPool
Started TaskId: 1 ThreadId: 17292
Started TaskId: 1 ThreadId: 17292
Started TaskId: 2 ThreadId: 19256
Started TaskId: 3 ThreadId: 25268
Started TaskId: 3 ThreadId: 25268
Started TaskId: 4 ThreadId: 21112
TaskId 1 completed. ThreadId: 17292
Started TaskId: 5 ThreadId: 17292
TaskId 2 completed. ThreadId: 19256
Started TaskId: 6 ThreadId: 19256
TaskId 3 completed. ThreadId: 25268
TaskId 3 completed. ThreadId: 25268
TaskId 4 completed. ThreadId: 21112
Started TaskId: 7 ThreadId: 21112Started TaskId: 8 ThreadId: 25268Started TaskId: 7 ThreadId: 21112Started TaskId: 8 ThreadId: 25268
TaskId 5 completed. ThreadId: 17292
Started TaskId: 9 ThreadId: 17292
TaskId 6 completed. ThreadId: 19256
Started TaskId: 10 ThreadId: 19256
TaskId 8 completed. ThreadId: 25268
TaskId 8 completed. ThreadId: 25268
TaskId 7 completed. ThreadId: 21112
TaskId 9 completed. ThreadId: 17292
TaskId 10 completed. ThreadId: 19256
All tasks completed.
The problem with the code is that the Start and Completion reporting is duplicated in certain cases:
Without looking too deeply, the problem is likely that you are using WriteLn
in multi-threaded code which can be prone to problems. Add some sort of synchronization to your output to ensure only one WriteLn
call is executed at a time.
var
ConsoleLock: TCriticalSection;
procedure SafeWriteLn(const Msg: string);
begin
ConsoleLock.Enter;
try
WriteLn(Msg);
finally
ConsoleLock.Leave;
end;
end;
ConsoleLock := TCriticalSection.Create;
try
//your stuff
finally
ConsoleLock.Free;
end;