Reputation: 481
I'm trying to learn how to use delphi parallel library instead of TThread. I have many tasks that I want to run simultaneously. Each task is most time waiting for some event, while its waiting, I'd like to pass excecution to another tasks. Here is my sample code:
type
TObj = class
pos: integer;
Done: boolean;
Constructor Create;
procedure DoWork(Sender: TObject);
end;
var
Objects : array [0..39] of TObj;
tasks : array of ITask;
constructor TObj.Create;
begin
pos:=0;
DOne:=false;
end;
procedure TObj.DoWork(Sender: TObject);
begin
repeat
inc(pos);
sleep(100);
until Done;
end;
procedure TForm1.StartClick(Sender: TObject);
var
i: integer;
begin
Setlength (tasks ,Length(Objects));
for i:=0 to Length(tasks)-1 do begin
Objects[i]:=TObj.Create;
tasks[i] := TTask.Create(Objects[i],Objects[i].DoWork);
tasks[i].Start;
end;
sleep(5000);
Memo1.Lines.Clear;
for i:=0 to Length(tasks)-1 do begin
Objects[i].Done:=true;
Memo1.Lines.Add(IntToStr(Objects[i].pos));
end;
end;
I start N Tasks, each should increase its counter by 1 in 100ms. Then I wait 5 sec and check task's values. I excpect them to be at least nearly equal, but real Memo1's output shows first 12 values are about 50, the other 4 values about 20-30 (I'm running Ryzen 1600), and whats strange fo me that the least values are all 0!
Evidently only 16 tasks from 40 actually excecuted atleast once in 5 seconds, so I would like to know how I can replace sleep(100) to actually pass excecution to another tasks?
Upvotes: 2
Views: 5075
Reputation: 597941
Just because you start N number of tasks does not mean that N number of tasks will be running concurrently. If you want that, stick with TThread
.
The PPL uses an internal thread pool to service the TTask
objects, and that pool is self-throttling and self-adjusting based on now many CPUs you have installed and how many tasks are actually running. This is explained in the PPL documentation:
The RTL provides the Parallel Programming Library (PPL), giving your applications the ability to have tasks running in parallel taking advantage of working across multiple CPU devices and computers. The PPL includes a number of advanced features for running tasks, joining tasks, waiting on groups of tasks, etc. to process. For all this, there is a thread pool that self tunes itself automatically (based on the load on the CPU’s) so you do not have to care about creating or managing threads for this purpose.
If you create more tasks than the pool has threads, some tasks are going to be waiting for earlier tasks to finish their work. When a given thread finishes a task, it checks for a waiting task, and if found then runs it, repeating until there are no more tasks to run. Multiply that by the number of threads in the pool and the number of tasks in the queue.
So, there will only ever be a handful of tasks running at any given time, and so it may take awhile to get through all of the tasks that you queue up.
If you want more control over the thread pooling, you have to create a TThreadPool
object, set its MinWorkerThreads
and MaxWorkerThreads
properties as desired, and then pass that object to one of the TTask
constructors that has an APool
input parameter. By default, the MinWorkerThreads
is set to TThread.ProcessorCount
, and the MaxWorkerThreads
is set to TThread.ProcessorCount * 25
.
Upvotes: 7