I have some low-priority tasks that are working in background, I dont care about how fast they are executed, but sometimes there is a single task that must be executed as fast as possible. How i do that?
for k:=1 to 100 do begin
Orders[k] := TTask.Create(NewOrder,NewOrder.DoTheJob);
Orders[k].Start;
end;
......
FastOrder := TTask.Create(NewOrder,NewOrder.DoTheJob);
FastOrder.Start; // I want this task to be executed ASAP,
//but actually it waits unpredictable time until there is a free slot
// in task's queue or so.
//It can delay up to 1 minute depending on how many reqular tasks i have
Use TThread.createAnonymousThread instead of TTask
Related
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?
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.
I'm a novice in Multithreading, i have a procedure that goes through a series of operations using indy (login to a site and download files and ...), sometimes when a site is not responsive or it takes too long, no exception occurs even with idHttp.connectTimeout and idHttp.readTimeout set to a specific amount or sometimes it does occur but not at the time i have specified !!, which is why i prefer to check the task and see if for example an ITask is running for 30 sec, it must be terminated !, i tried using itask.cancel but it only cancels if something is in queue and doesn't terminate a task that is already running, what is the solution to my problem ?
just so you know what is happening in my program :
originally my program creates classes based on a list of profiles and starts a process of login and download, i want to terminate any startDownloadProcess that takes more than 30 sec
here is a code example :
// this is what i used to do
for I := 0 to mProfileList.count - 1 do
begin
myClass := TMyClass.create(mProfileList[i]);
//sometimes this takes a very very long time and i don't want that.
myClass.startDownloadProcess;
end;
// here is what i have in mind
for I := 0 to mProfileList.count - 1 do
begin
mITaskArray[i] := TTask.run(procedure
begin
myClass := TMyClass.create(mProfileList[i]);
myClass.startDownloadProcess;
end);
end;
What i need :
i want each task to depend on the previous one (a queue) and wait until the previous one completes, and if a task takes more than 30 second, it terminates and the queue continues.
now i know there might be a lot of things wrong with that code but that is why i'm here, i'm a bit lost about how to proceed or if what i'm doing is correct at all !
you say you are new in multithreading, so remember to never (never) use the TTASK/ITask or similar TParalel. it's completely buggy ! use instead TAnonymousThread
MyThread := TThread.createAnonymousThread(
Procedure
Begin
repeat
if MyThread.checkterminated then exit;
if moreThan30secondsRunning then exit;
....
until wordDone;
end).start;
I am using TThread, i just need to find out if thread is still running and terminate the application if it takes to long.
Ex:
MyThread := TMyThread.Create;
MyThread .Resume;
MyThread.Terminate;
total := 0;
while MyThread.isRunning = true do // < i need something like this
begin
// show message to user to wait a little longer we are still doing stuff
sleep(1000);
total := total + 1000;
if total > 60000 then
exit;
end;
Is this possible with Delphi?
The straight answer to your question is the Finished property of TThread. However, I don't recommend that. It leads you to that rather nasty Sleep based loop that you present in the question.
There are a number of cleaner options, including at least the following:
Use WaitFor to block until the thread completes. This will block the calling thread which will preclude showing UI, or responding to the user.
Use MsgWaitForMultipleObjects in a loop, passing the thread handle. This allows you to wait until the thread completes, but also service the UI.
Implement an event handler for the OnTerminate event. This allows you to be notified in an event driven manner of the thread's demise.
I recommend that you use one of these options instead of your home-grown Sleep based loop. In my experience, option 3 generally leads to the cleanest code. But it is hard to give rigid advice without knowing all the requirements. Only you have that knowledge.
Let's say I have to recursively iterate over items stored in a tree structure in the background and I want to walk this tree using multiple threads from a thread pool (one thread per "folder" node). I have already managed to implement this using several different low and high-level approaches provided by the OmniThreadLibrary.
However, what I haven't figured out yet is how to properly detect that the scan has actually completed, i.e. that every last leaf node has been processed.
I found various examples on the net that either checked whether GlobalThreadPool.CountExecuting + GlobalThreadPool.CountQueued <= 0 or that used a IOmniTaskGroup.WaitForAll(). Unfortunately, none of these approaches appears to work for me. The check always returns True too early, i.e. when there still are some tasks running. None of the examples I looked at used recursion though - and those that did did not use a thread pool - is this maybe just not a good combination in the first place?
Here's a (very) simplified example code snippet of how I'm trying to do this at the moment:
procedure CreateScanFolderTask(const AFolder: IFolder);
begin
CreateTask(ScanFolder)
.SetParameter('Folder', AFolder)
.Schedule();
end;
procedure ScanFolder(const ATask: IOmniTask);
var
lFolder,
lCurrentFolder: IFolder;
begin
if ATaks.CancellationToken.IsSignalled then Exit;
lCurrentFolder := ATask.Param['Folder'].AsInterface as IFolder;
DoSomethingWithItemsInFolder(lCurrentFolder.Items);
for lFolder in lCurrentFolder.Folders do
begin
if ATaks.CancellationToken.IsSignalled then Exit;
CreateScanFolderTask(lFolder);
end;
end;
begin
GlobalOmniThreadPool.MaxExecuting := 8;
CreateScanFolderTask(FRootFolder);
// ??? wait for recursive scan to finish
OutputResult();
end.
One example implementation for the wait that I have tried was this (based on an example found on About.com):
while GlobalOmniThreadPool.CountExecuting + GlobalOmniThreadPool.CountQueued > 0 do
Application.ProcessMessages;
But this appears to always exit immediately right after the "root thread" has finished. Even when I add an artificial delay using Sleep()-calls it still always exits too early. It seems that there occurs a "gap" between one task being struck off the list of executing tasks and the ones that were scheduled inside that task to be added to the list of queued tasks...
Actually, instead of waiting for the scan to finish, I would very much prefer to use an event handler (also, I'd rather not use Application.ProcessMessages as I will need this in form-less applications, too) and I already did try with both IOmniTaskControl.OnTerminated and using a TOmniEventMonitor but as these fire for every finished task I still somehow need to check whether the current one was the last one which again boils down to the same problem as above.
Or is there maybe a better way to create the tasks that would avoid this problem?
A simple way is to count 'folders to be processed' by yourself. Increment a value every
time you create a folder task and decrement it every time a folder is processed.
var
counter: TOmniCounter;
counter.Value := 0;
procedure ScanFolder(const ATask: IOmniTask);
var
lFolder,
lCurrentFolder: IFolder;
begin
if ATaks.CancellationToken.IsSignalled then Exit;
lCurrentFolder := ATask.Param['Folder'].AsInterface as IFolder;
DoSomethingWithItemsInFolder(lCurrentFolder.Items);
for lFolder in lCurrentFolder.Folders do
begin
if ATaks.CancellationToken.IsSignalled then Exit;
counter.Increment;
CreateScanFolderTask(lFolder);
end;
counter.Decrement;
end;
What I usually do is to count all the issued 'folderScan' objects out and count them back in again.
Each time a new TfolderScan is needed, the creating TfolderScan calls a factory for it The factory increments a CS-protected 'taskCount' as well as creating the TfolderScan. Every time a TfolderScan is completed, it calls the 'OnComplete' method of the factory that decrements the CS-protected 'taskCount'. If, in 'OnComplete', the count is decremeted to 0, there can be no TfolderScan left and the whole search most be complete. The thread that manages to decrement the count to 0 can do whatever is needed to signal the completion - PostMessage() a main form or call an 'OnSearchComplete' event.
Just as a side-note : Instead of checking (GlobalOmniThreadPool.CountExecuting + GlobalOmniThreadPool.CountQueued > 0) use (not GlobalOmniThreadPool.IsIdle). This hides implementation details and is more efficient.
I'm developing an application that should perform certain sql queries in different MSSQL servers after specified intervals. My thoughts are about make an array of threads and in each thread run timer in which task will run. Does it really needs to make each thread for each timer, or should I just distinguish one timer for each task (whatever timer will produce thread)? Any thoughts about implementation?
Thanks a lot, guys!
I doubt that you need to have one thread per task. It would probably suffice to create one timer per task. If a timer fires whilst another task is running then the second task will have to queue up but it doesn't sound like that will be a great problem.
If you are going to use a Delphi TTimer to do this you'll need to make sure that your service has a message queue and runs a message loop on that queue. You may wish to run that message queue on a separate thread but if you do make sure that the TTimer objects are created on that thread so that they are associated with the right message queue.
You ask in the comments how to run a message loop in a thread. The following code should suffice:
repeat
try
Application.HandleMessage;
except
Application.HandleException(Application);
end;
until Terminated;//this is the Terminated property of the thread
This will give you all the bells and whistles of the Delphi message loop. If you want a very standard message loop you can use this:
procedure PerformThreadLoop;
var
Msg: TMsg;
begin
repeat
Try
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
WaitMessage;
Except
Application.HandleException(Self);
End;
until Terminated;//this is the Terminated property of the thread
end;
If all you want is to pump WM_TIMER messages both will work but I personally would be inclined to go with the second option, the raw Win32 API version.