TTask in Delphi 10.2 - multithreading

not used TTask before and its a simple thing im trying to do.
While the main form executes a procedure (DoAnalyse) id like to display the TActivityIndicator on my main form without it slowing as the thread does the work. I used to use a progress bar but would prefer to use the more modern Activity Indicator.
I Have tried it two ways:
A simple:
Ttask.Run(DoAnalyse);
and
Task := TTask.Create( procedure
begin
DoAnalyse;
end);
Task.Start;
it executes perfectly and my activity indicator behaves smoothly.
Problem is when the procedure is finished the main form is locked and doesn't respond at all. im guessing I have to put some kind of call back to the main thread but cant find out how to do it.
Any ideas?

TTask doesn't have an event when it stops running. Your task procedure needs to use TThread.Synchronize() or TThread.Queue() (or any other inter-thread mechanism of your choosing) to notify the main thread before it exits.
Otherwise, use TThread instead of TTask. TThread has an OnTerminate event.

Related

How Do I Call a Thread.Execute in GUI Thread?

There is a TThread descendant class with its own Execute method doing some math. It works fine but I am looking for the optimization of the following kind. The GUI thread and context of the program determine the count of necessary instances of these threads to be created, run and freed. In certain (rare or user determined) circumstances creation of one instance is enough.
By the moment I use the following construction (in pseudocode):
if ThreadsCount>1 then
begin
Creation of threads
starting them
waiting for results
evaluating and assigning the best result
freeing the threads
end
else
starting the math procedure (edited here separately)
// and in MyThread class declaration
procedure Execute... (edited here separately)
So there are two places in code that have my math procedure and I have to edit both of them if some math changes are applied. The GUI math procedure is a bit different from that one called in thread so I can not simply extract the method and call it.
I wonder if there is a way to create a thread instance and call its Execute method in GUI thread?
You could write some seriously hacky, indescribably bad code to enable you to safely call a TThread's Execute(). But it's an absurd thing to do. The whole point of the TThread class is that it:
starts a new thread in the OS;
then calls Execute() on that thread.
So:
If you don't need a thread, there's absolutely no point in starting a thread that you don't want to use.
You would need to prevent Execute() from doing any processing on its thread-run.
You could then call Execute from the main thread.
But since you have no guarantees how long the thread will take to not do any processing when it calls Execute(), you'd still need to wait for the thread to finish before you can destroy the TThread object.
The GUI math procedure is a bit different from that one called in thread so I can not simply extract the method and call it.
This makes absolutely no sense.
If your two "math procedures" are different, then trying to call the thread-implementation from GUI would change the behaviour of your program. Conversely, if you can reuse the thread-implementation, then you most certainly can also extract the method! (Or at the very least the common elements.)
Caution
That said, there is some caution required when sharing code that might run in a TThread.Execute(). Any code that must run on the main thread needs to be synchronised or queued. Inside TThread objects, you'd simply call the Synchronize() or Queue() method. However, shared code shouldn't be on a TThread object making things a little trickier.
To resolve this, you can use the the Synchronize() and Queue() class methods on TThread. This allows you to synchronise without instantiating a TThread instance. (Note these methods are safe to call from the main thread because they would simply call the sync method directly in that case.)
Code along the following lines should do the trick.
Implement your shared code in a suitable object. This is conceptually a runnable object, and something you may want to research.
TSharedProcess = class
private
{ Set this if the process is run from a child thread,
leave nil if run from main thread. }
FThread: TThread;
procedure SyncProc();
public
procedure Run();
property Thread: TThread read FThread write FThread;
end;
procedure TSharedProcess.Run();
begin
...
TThread.Synchronize(FThread, SyncProc);
...
end;
When you want to run the shared code from the main thread, the following is an option.
begin
LProc := TSharedProcess.Create(...);
try
LProc.Run();
finally
LProc.Free;
end;
end;
To run from a child thread a simple thread wrapper will suffice. And then you can create the runnable object in the main thread, and pass it to the thread wrapper.
{ TShardProcessThread for use when calling from child thread. }
constructor TSharedProcessThread.Create(AProc: TSharedProcessThread);
begin
FProc := AProc;
FProc.Thread := Self;
inherited;
end;
procedure TShardProcessThread.Execute();
begin
FProc.Run();
end;
{ Main thread creates child thread }
begin
{ Keep reference to FProc because it can only be destroyed after
thread terminates.
TIP: Safest would be to use a reference counted interface. }
FProc := TSharedProcess.Create(...);
try
LThread := TShardProcessThread.Create(FProc);
LThread.OnTerminate := HandleThreadTerminate;
except
{ Exception in thread create means thread will not run and
will not terminate; so free object immediately. }
FProc.Free;
raise;
end;
end;
Disclaimer
I have not tested this code because I see no benefit in doing something like this. Users gain nothing by being able to force code to run on the main thread. Furthermore the paradigms for synchronous code are fundamentally different to asynchronous code. Trying to implement a hybrid reduces maintainability by cluttering your 'business code' with technical detail.
Use at your own risk.
The way to approach this problem is to extract into a method the code that you need to perform either in a worker thread or the main thread. You can then call that code either from your worker thread's Execute method, or from your main thread code.

How to prevent freezing MainForm and wait for return value from sub thread

I have thread where downloading xml file a this xml file i want to parse in other class. Now I have prepare thread, where I can download it.
In MainForm I'm using WaitForSingleObject, but this function freeze MainFrom, so how can I prevent reezing and wait for value?
Thanks.
First, design your thread with events and invoke them using Synchronize.
Create thread in MainForm and assign event handlers to it:
Thread := TMyThread.Create(True);
Thread.OnDownload := DoDownload;
Thread.OnParse := DoParse;
// Other Events
Thread.Start;
You should not WaitFor* in MainForm. Avoid Windows messages method because your app will loose platform portability, just linked to WinXX compilation.
It's not really a good design to wait on the main thread, but in some cases it can be necessary. However, while you wait, you still need to process messages coming to the thread.
Take a look at MsgWaitForMultipleObjects at MSDN.

Periodical tasks in Delphi app

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.

Can I execute TDataSet.DisableControls in worker thread without wrapping it with Synchronize()?

First of all, I am not sure that it is a good design to allow worker thread to disable controls. However, I am curious can I do it safely without synchronization with GUI?
The code in TDataSet looks like this:
procedure TDataSet.DisableControls;
begin
if FDisableCount = 0 then
begin
FDisableState := FState;
FEnableEvent := deDataSetChange;
end;
Inc(FDisableCount);
end;
So it looks safe to do. The situation would be different in case of EnableControls. But DisableControls seems to only increase lock counter and assigning event which is fired up during EnableControls.
What do you think?
It looks safe to do so, but things may go wrong because these flags are used in code that may be in the middle of being executed at the moment you call this method from your thread.
I would Synchronise the call to DisableControls, because you want your thread to start using this dataset only if no controls are using it.
The call to EnableControls can be synchronised too, or you can post a message to the form using PostMessage. That way, the thread doesn't have to wait for the main thread.
But my gut feelings tells me that is may be better to not use the same dataset for the GUI and the thread at all.
Without having looked up the actual code: It might be safe, as long as you can be sure that the main thread currently does not access FDisableCount, FDisableState and FEnableEvent. There is the possibility of a race condition here.
I would still recommend that you call DisableControls from within the main thread.

how to write and execute a thread

I have never worked with threads before, and now I'm trying to create a thread with a query to check database status. The query is as follows:
select (*) as DBCount from v$datafile where status in 'OFFLINE';.
This query returns the total number of all the databases that are offline. Now I want to create a thread in Delphi that will execute this query in the background of my application when I run it and display the results on a label.
Multithreading is hard. You may be better off using a simple threading framework like AsyncCalls.
Following StackOverlow threads may give you more information on how to solve your problem:
Delphi - Threading frameworks
How Do I Choose Between the Various Ways to do Threading in Delphi?
Delphi thread that waits for data, processes it, then resumes waiting
This is fairly easy to do using AsyncCalls. Let's assume your non-threaded code looks like this (ignoring all error handling):
Query := 'select (*) as DBCount from...';
ExecuteSQL(SqlConnection,Query);
SqlResult := GetSqlResult(SqlConnection);
SqlRow := GetSqlRow(SqlResult);
MyLabel.Text := SqlRow[0];
...Go on to do other things...
Where the second line is blocking (waiting for the server to reply). Your new code would look like this:
uses AsyncCalls; //added to your existing uses statement
...
procedure DoesSomething();
var Thread: TAsyncCall; //your interface to AsyncCalls
procedure AsyncSqlCall(); //this is a LOCAL procedure
Query := 'select (*) as DBCount from...';
ExecuteSQL(SqlConnection,Query);
SqlResult := GetSqlResult(SqlConnection);
SqlRow := GetSqlRow(SqlResult);
EnterMainThread;
try
Assert(GetCurrentThreadId = MainThreadId);
MyLabel.Text := SqlRow[0];
finally
LeaveMainThread;
end;
begin //this begins proc DoSomething()
...
Thread := LocalAsyncCall(#AsyncSqlCall);
...Go on to do other things...
end;
All we've done is put the blocking SQL calls in a local proc and told AsyncCalls to execute it in another thread, while the main thread continues executing. The only tricky part was using the VCL, which is not thread-safe. So I had that line safely run in the main thread.
If at some point you need to be sure the Async thread has completed, you would execute this line to block the main thread till AsyncSqlCall terminates:
Thread.sync;
The really nice thing here is that AsyncCalls handles all the stuff about creating a thread pool, creating threads, etc. Though not shown in this example, you can pass variables to your thread, and return a value. You don't have to use a local proc, but doing so gives it access to all local vars. You could make all this global, and then launch the Async thread in one routine, and test for its completion in another.
Restrictions:
Your Async thread must not touch (read or write) anything but its own variables, and your main thread must not touch them while the Async thread is running. YOU must code it that way. Nothing will stop you from creating total chaos. In the above example, your main thread must not touch Query, SqlConnection, SqlResult, and SqlRow. If any part of your code used one of those vars before the Thread.sync call, your code would work -- but throw exceptions in weird places you never expected. So keep it simple.
Your Async thread must not use the VCL. The sample above shows one of several ways to safely get around this limitation.
Finally:
AsyncCalls is not a full Multi-Threading framework. It's just a way of calling procs & functions asynchronously (i.e. without waiting). Don't try to push it too far -- by which I mean, don't try to make it the basis of a fully multitasking program.
In Delphi there is TThread class. You can create such thread using simple wizard from Delphi IDE (in Turbo Delphi: File->New->Delphi Projects->Delphi Files->Thread Obcjet). In its constructor create connection to database, and in Execute method you can create loop that query database, update some global variable (NOT declared as threadvar) and sleep some time.
Here you can find a discussion about using threads on DataBase.
Simple Thread Sample Delphi
There are some code that can be usefull for you.
Regards.

Resources