In my last post I spoke about TTask and how it enables us developers to quickly run multiple tasks at the same time with limited bottleneck in our applications. Moving on from that I want to explore IFuture which impletements ITask.
IFuture
IFuture , provides TTask with a structure us developers can use to creating a function that returns a specific type (defined using Generics, thats the <T> bit you see in code sometimes). Using an instance of IFuture, the process can run and then allow us to get other stuff done, until such point as we need the result. This allows us to prioritise code blocks to run in the order we want them to, but still ensure we get the value we need at the point we need it!
Example
To get a value in the future, you first need to define what type of value, set it running and then go call it. To view this, below I am using a totally pointless (but shows how to use this feature) block of code, which I will break down step by step afterwards.
procedure TFormThreading.Button3Click(Sender: TObject); var OneValue: IFuture <Integer>; OtherValue: Integer; Total: Integer; begin OneValue := TTask.Future<Integer>(function: Integer begin Result := ComputeSomething; Sleep(1000); // delay to show status end); Memo1.Lines.Add(TRttiEnumerationType. GetName<TTaskStatus>(OneValue.Status)); OtherValue := ComputeSomething; Memo1.Lines.Add(TRttiEnumerationType. GetName<TTaskStatus>(OneValue.Status)); Total := OtherValue + OneValue.Value; Memo1.Lines.Add(TRttiEnumerationType. GetName<TTaskStatus>(OneValue.Status)); // result Memo1.Lines.Add(Total.ToString); end;
The output of this code looks something like this..
Key points in the code
The first step, is using TTask.Future<T> to define the type to be returned, and then pass in the anonymous method to return the instance of that value. (Here we are getting an Integer from ComputeSomething so we use Integer as the type)
The output of calling TTask.Future is an instance of IFuture into the OneValue variable defined.
OneValue := TTask.Future<Integer>(function: Integer begin Result := ComputeSomething; Sleep(1000); // delay to show status end);
OK, so putting a Sleep command in the anonymous method here is kind of pointless, but it does allow when running this demo code to see the result of the call to OneValue.Status change from WaitingToRun, to Running, to Completed.
As you read down further, you will see OneValue being queried for its current status. The code for converting our Future’s Status to a string is the same as any other Enumeration type, pass in the type you want to convert and the value to GetName.
TRttiEnumerationType. GetName<TTaskStatus>(OneValue.Status)
The first value returned will be WaitingToRun as everything is prepared. Following the first status query, we call the same ComputeSomething task
OtherValue := ComputeSomething;
Afterwards, we can check the status of OneValue and see that (due to the sleep taking longer than the ComputeSomething call) its now reporting as running.
So hold on! Does that mean we need to check the status to see if its OK to get the value? Well actually NO 🙂
Total := OtherValue + OneValue.Value;
This line asks OneValue for its Value. If it is done, it will have the value ready for you, if not (as in this case) it will block and wait for IFuture to finish before executing the code making life very easy on us developers.
So thats IFuture, its a process that you can set running, but will return at the point it is ready. Another way to save time and speed up your application code.
Hi, I did this:
var
tsk: ITask;
begin
tsk := TTask.Future(
function: Boolean
begin
result := (0>1);
end);
if tsk.value then
begin
end;
BUT the compiler says: ITASk does not contain a member named Value.
The variable type is wrong?
You have mixed an ITask and an IFuture. try
var; (
tsk: IFuture
begin
tsk := TTask.Future
function: Boolean
begin
result := (0>1);
end
);
if tsk.value then
begin
end;
end;
Hello Stephen,
Is possible to update an UI control while the TTask.Future is running?
I want to update a progress bar while the TTask.Future is running. But I can’t found documentation about that.
You need to Syncronize with the main thread. You can do this with an anonymous procedure using TThread.Syncronize http://docwiki.embarcadero.com/Libraries/en/System.Classes.TThread.Synchronize