In the example below (from Embarcadero's manual), the MyValue will be retrieved after 3 seconds about. But, the main gui thread would be blocked by the MyValue := FutureObject.Value; call which will wait for the result.
If the Future code will take long, lets say 30 seconds, Windows will show a "... not responding" in the programs caption I guess.
So whats the purpose of this when it will block the main gui thread?
Is there any other way to get the result without blocking the main gui thread?
FutureObject := TTask.Future<Integer>(function: Integer
begin
Sleep(3000);
Result := 16;
end);
// …
MyValue := FutureObject.Value;
By design, the IFuture.Value property blocks the calling thread until another thread assigns a value. So, if you read Value in the main thread, it is going to block the main thread until a value is ready.
If you must read Value in the main thread without blocking, you can either:
use a timer or other asynchronous mechanism to periodically query the IFuture.Status property to check when the IFuture is ready to provide a value before actually reading it.
have the parallel task signal the main thread when it is ready, and then the main thread can read the Value in its signal handler.
I know it might be a bit late but i solved it by creating a TTask that did the
MyValue := FutureObject.Value
FutureObject := TTask.Future<Integer>(function: Integer
begin
Sleep(3000);
Result := 16;
end);
// …
TTask.Run(Procedure begin
MyValue := FutureObject.Value;
end);
Related
I have built the backend (with WebBroker) and it has some APIs. So in the client I am going to use the REST components to get the json and parse it. This is an example:
procedure TForm1.ButtonCreateClick(Sender: TObject);
begin
//rreqTodoCreate is a TRESTRequest component!
rreqTodoCreate.Params[0].Value := EditTitle.Text;
rreqTodoCreate.Params[1].Value := EditCategory.Text;
rreqTodoCreate.ExecuteAsync(procedure
begin
ResponseEdit.Text := rrespToDo.Content;
end);
end;
I am using the ExecuteAsync as the docwiki suggests also because I am on mobile and I don't want the UI to freeze! I have read that ExecuteAsync runs on a separate thread and so I have a doubt.
Is the code I have written thread-safe? Or in other words: should I use Queue or Synchronize when I update the text of a component in the main form?
Per the TRESTRequest.ExecuteAsync() documentation:
Parameters
This method defines the following parameters:
ACompletionHandler -- Specifies an anonymous method to run after completing the request execution.
ASynchronized -- When True, specifies that the method set in ACompletionHandler runs in the main thread context. When False, ACompletionHandler runs in the execution thread context.
AFreeThread - When True, the execution thread is freed after completing the request execution.
The ASynchronized parameter is True by default:
function ExecuteAsync(
ACompletionHandler: TCompletionHandler = nil;
ASynchronized: Boolean = True; // <--
AFreeThread: Boolean = True;
ACompletionHandlerWithError: TCompletionHandlerWithError = nil): TRESTExecutionThread;
So, the code you have shown is perfectly fine as-is, the assignment of the ResponseEdit.Text is thread-safe.
Yes, I also think that this is better:
rreqTodoCreate.ExecuteAsync(procedure
begin
TThread.Queue(procedure
begin
ResponseEdit.BeginUpdate;
ResponseEdit.Text := rrespToDo.Content;
ResponseEdit.EndUpdate;
end;
end);
Explanation:
TThread.Queue executes the anonymous method in the main thread
BeginUpdate and EndUpdate freeze the UI and speed up the update of form
I am using CreateAnonymousThread for a worker task, and when I started with it I used Synchronize within the entire declaration as per documented examples, e.g:
procedure Txxx.RunWorker;
begin
FExecutionThread := TThread.CreateAnonymousThread(procedure ()
begin
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
// Here before worker stuff
NotifyBeforeWorkerStuff;
end);
// Do worker stuff
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
// Here after worker stuff
NotifyAfterWorkerStuff;
end);
end);
FExecutionThread.Start;
end;
end;
As you see, from within this thread I launch event notifications to various parts of my app including VCL forms (NotifyBeforeWorkerStuff etc).
Later, I saw that I could move Synchronize() more locally to each VCL form close to the point that actually required it for updating (non-safe) VCL controls:
procedure TSomeVCLForm.ReceiveNotification;
begin
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
Label1.Caption := GetSomeStringFunction;
end);
end;
The worker thread then becomes simpler as long as I live with notifications being from either main or worker threads:
procedure Txxx.RunWorker;
begin
FExecutionThread := TThread.CreateAnonymousThread(procedure ()
begin
NotifyBeforeWorkerStuff;
// Do worker stuff
NotifyAfterWorkerStuff;
end);
FExecutionThread.Start;
end;
I have several questions about whether this is correct:
My notifications may be from the worker thread but also from the main thread (e.g derived from a button press). So, when 'ReceiveNotification' on a VCL form is called from the main thread, is it allowed to call TThread.Synchronize as above? The the XE8 docs imply not, but checking System.Classes this looks ok and it works fine.
Within 'ReceiveNotification' when Label1.Caption fetches the string from GetSomeStringFunction, is it correct that there is absolutely no need for locking within that function even when the call is from a worker thread?
Thanks for any advice.
The documentation says:
Warning: Do not call Synchronize from within the main thread. This can cause an infinite loop.
I think that documentation is simply wrong. The implementation in XE8 checks whether or not the current thread is the main thread. If it is then the method is executed directly.
No locking is required in ReceiveNotification because the call to GetSomeStringFunction is always performed on the main thread.
I came across this piece of Delphi code while searching for background methods to execute tasks in firemonkey.
TThread.CreateAnonymousThread(
procedure()
begin
Sleep(10000);
TThread.Synchronize(TThread.CurrentThread,
procedure
begin
Button2.Text := 'OK';
end);
end).Start;
Is TThread.Synchronize at this case really necessary?
TButton.Text changes a property of a Window object, which is inherently non-thread-safe, and is only to be accessed directly from the thread which created it OR via message sends/posts.
What TThread.Synchronize does is - it wraps the procedure together with a waitable object, places this to a queue, and waits on the handle - it may post a message to the main thread to wake it up.
If the code behind TButton.Text was implemented via posted message - and is is not - it would be safe to call from other threads, but it would not take effect immediately.
Long story short - you definitely have to call this via Synchronize, for good reasons.
I am developing a single thread app with Delphi, which will do a time-consuming task, like this:
// time-consuming loop
For I := 0 to 1024 * 65536 do
Begin
DoTask();
End;
When the loop starts, the application will lose responses to the end user. That is not very good. I also do not want to convert it into a multi-thread application because of its complexity, so I add Application.ProcessMessages accordingly,
// time-consuming loop
For I := 0 to 1024 * 65536 do
Begin
DoTask();
Application.ProcessMessages;
End;
However, this time although the application will response to user operations, the time-consumed in the loop is much more than the original loop, about 10 times.
Is there a solution to make sure the application does not lose the response while do not increase the consumed time too much?
You really should use a worker thread. This is what threads are good for.
Using Application.ProcessMessages() is a band-aid, not a solution. Your app will still be unresponsive while DoTask() is doing its work, unless you litter DoTask() with additional calls to Application.ProcessMessages(). Plus, calling Application.ProcessMessages() directly introduces reentrant issues if you are not careful.
If you must call Application.ProcessMessages() directly, then don't call it unless there are messages actually waiting to be processed. You can use the Win32 API GetQueueStatus() function to detect that condition, for example:
// time-consuming loop
For I := 0 to 1024 * 65536 do
Begin
DoTask();
if GetQueueStatus(QS_ALLINPUT) <> 0 then
Application.ProcessMessages;
End;
Otherwise, move the DoTask() loop into a thread (yeah yeah) and then have your main
loop use MsgWaitForMultipleObjects() to wait for the task thread to finish.
That still allows you to detect when to process messages, eg:
procedure TMyTaskThread.Execute;
begin
// time-consuming loop
for I := 0 to 1024 * 65536 do
begin
if Terminated then Exit;
DoTask();
end;
end;
var
MyThread: TMyTaskThread;
Ret: DWORD;
begin
...
MyThread := TMyTaskThread.Create;
repeat
Ret := MsgWaitForMultipleObjects(1, Thread.Handle, FALSE, INFINITE, QS_ALLINPUT);
if (Ret = WAIT_OBJECT_0) or (Ret = WAIT_FAILED) then Break;
if Ret = (WAIT_OBJECT_0+1) then Application.ProcessMessages;
until False;
MyThread.Terminate;
MyThread.WaitFor;
MyThread.Free;
...
end;
You say :
I also do not want to convert it into a multi-thread application
because of its complexity
I can take this to mean one of two things :
Your application is a sprawling mess of legacy code that is so huge and so badly written that encapsulating DoTask in a thread would mean an enormous amount of refactoring for which a viable business case cannot be made.
You feel that writing multithreaded code is too "complex" and you don't want to learn how to do it.
If the case is #2 then there is no excuse whatsoever - multithreading is the clear answer to this problem. It's not so scary to roll a method into a thread and you'll become a better developer for learning how to do it.
If the case is #1, and I leave this to you to decide, then I'll note that for the duration of the loop you will be calling Application.ProcessMessages 67 million times with this :
For I := 0 to 1024 * 65536 do
Begin
DoTask();
Application.ProcessMessages;
End;
The typical way that this crime is covered up is simply by not calling Application.ProcessMessages every time you run through the loop.
For I := 0 to 1024 * 65536 do
Begin
DoTask();
if I mod 1024 = 0 then Application.ProcessMessages;
End;
But if Application.ProcessMessages is actually taking ten times longer than DoTask() to execute then I really question how complex DoTask really is and whether it really is such a hard job to refactor it into a thread. If you fix this with ProcessMessages, you really should consider it a temporary solution.
Especially take care that using ProcessMessages means that you must make sure that all of your message handlers are re-entrant.
Application.ProcessMessages should be avoided. It can cause all sorts of strange things to your program. A must read: The Dark Side of Application.ProcessMessages in Delphi Applications.
In your case a thread is the solution, even though DoTask() may have to be refactored a bit to run in a thread.
Here is a simple example using an anonymous thread. (Requires Delphi-XE or newer).
uses
System.Classes;
procedure TForm1.MyButtonClick( Sender : TObject);
var
aThread : TThread;
begin
aThread :=
TThread.CreateAnonymousThread(
procedure
var
I: Integer;
begin
// time-consuming loop
For I := 0 to 1024 * 65536 do
Begin
if TThread.CurrentThread.CheckTerminated then
Break;
DoTask();
End;
end
);
// Define a terminate thread event call
aThread.OnTerminate := Self.TaskTerminated;
aThread.Start;
// Thread is self freed on terminate by default
end;
procedure TForm1.TaskTerminated(Sender : TObject);
begin
// Thread is ready, inform the user
end;
The thread is self destroyed, and you can add a OnTerminate call to a method in your form.
Calling Application.ProcessMessages at every iteration will indeed slow down performance, and calling it every few times doesn't always work well if you can't predict how long each iteration will take, so I typically will use GetTickCount to time when 100 milliseconds have passed (1). This is long enough to not slow down performance too much, and fast enough to make the application appear responsive.
var
tc:cardinal;
begin
tc:=GetTickCount;
while something do
begin
if cardinal(GetTickCount-tc)>=100 then
begin
Application.ProcessMessages;
tc:=GetTickCount;
end;
DoSomething;
end;
end;
(1): not exactly 100 milliseconds, but somewhere close. There are more precise ways to measure time like QueryPerformanceTimer, but again this is more work and may hinder performance.
#user2704265, when you mention “application will lose responses to the end user”, do you mean that you want your user to continue working around in your application clicking and typing away? In that case - heed the previous answers and use threading.
If it’s good enough to provide feedback that your application is busy with a lengthy operation [and hasn't frozen] there are some options you can consider:
Dissable user input
Change the cursor to “busy”
Use a progressbar
Add a cancel button
Abiding to your request for a single threaded solution I recommend you start by disabling user input and change the cursor to “busy”.
procedure TForm1.ButtonDoLengthyTaskClick(Sender: TObject);
var i, j : integer;
begin
Screen.Cursor := crHourGlass;
//Disable user input capabilities
ButtonDoLengthyTask.Enabled := false;
Try
// time-consuming loop
For I := 0 to 1024 * 65536 do
Begin
DoTask();
// Calling Processmessages marginally increases the process time
// If we don't call and user clicks the disabled button while waiting then
// at the end of ButtonDoLengthyTaskClick the proc will be called again
// doubling the execution time.
Application.ProcessMessages;
End;
Finally
Screen.Cursor := crDefault;
ButtonDoLengthyTask.Enabled := true;
End;
End;
In a given example I am receiving an exception when calling AThread.Free.
program Project44;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes, Windows;
type
TMyException = class(Exception);
var
AThread: TThread;
begin
AThread := TThread.Create(True);
try
AThread.FreeOnTerminate := True;
//I want to do some things here before starting the thread
//During the setup phase some exception might occur, this exception is for simulating purpouses
raise TMyException.Create('exception');
except
AThread.Free; //Another exception here
end;
end.
I have two questions:
How should I free AThread instance of TThread in a given example?
I don't understand, why TThread.Destroy is calling Resume before destroing itself. What is the point of this?
You can't set FreeOnTerminate to True and call Free on the thread instance. You have to do one or the other, but not both. As it stands your code destroys the thread twice. You must never destroy an object twice and of course when the destructor runs for the second time, errors occur.
What happens here is that since you created the thread suspended, nothing happens until you explicitly free the thread. When you do that the destructor resumes the thread, waits for it to complete. This then results in Free being called again because you set FreeOnTerminate to True. This second call to Free closes the handle. Then you return to the thread proc and that calls ExitThread. This fails because the thread's handle has been closed.
As Martin points out in the comment you must not create TThread directly since the TThread.Execute method is abstract. Also, you should not use Resume which is deprecated. Use Start to begin execution of a suspended thread.
Personally I don't like to use FreeOnTerminate. Using this feature results in the thread being destroyed on a different thread from which it was created. You typically use it when you want to forget about the instance reference. That then leaves you uncertain as to whether or not the thread has been destroyed when your process terminates, or even whether it is terminating and freeing itself during process termination.
If you must use FreeOnTerminate then you need to make sure that you don't call Free after having set FreeOnTerminate to True. So the obvious solution is to set FreeOnTerminate to True immediately before after calling Start and then forget about the thread instance. If you have any exceptions before you are ready to start then you can safely free the thread then since you FreeOnTerminate would still be False at that point.
Thread := TMyThread.Create(True);
Try
//initialise thread object
Except
Thread.Free;
raise;
End;
Thread.FreeOnTerminate := True;
Thread.Start;
Thread := nil;
A more elegant approach would be to move all the initialisation into the TMyThread constructor. Then the code would look like this:
Thread := TMyThread.Create(True);
Thread.FreeOnTerminate := True;
Thread.Start;
Thread := nil;
The situation is very complicated in your case.
First, you does not actually free a suspended thread; a thread is resumed in destructor:
begin
Terminate;
if FCreateSuspended then
Resume;
WaitFor;
end;
Since Terminate is called before Resume, the Execute method never runs, and thread terminates immediately after being resumed:
try
if not Thread.Terminated then
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
finally
Result := Thread.FReturnValue;
FreeThread := Thread.FFreeOnTerminate;
Thread.DoTerminate;
Thread.FFinished := True;
SignalSyncEvent;
if FreeThread then Thread.Free;
Now look at the last line - you call destructor (Thread.Free) from destructor itself!
Fantastic bug!
To answer your questions:
You just can't use FreeOnTerminate:= True in your code;
You should ask Embarcadero why TThread is designed so; my guess - some code
(DoTerminate method) should be executed in thread context while
thread terminates.
You can send a feature request to QC: add FFreeOnTerminate:= False to TThread.Destroy implementation:
destructor TThread.Destroy;
begin
FFreeOnTerminate:= False;
// everything else is the same
..
end;
That should prevent recursive desctructor call and make your code valid.