WTF is a Future?

Of the abstractions introduced in the Parallel Programming Library, the IFuture<T> seems to be the one that many people have difficulty understanding.

Both TTask and TParallel.For seem reasonably easy to understand conceptually, even if you don’t understand all the details. TTask is about some code you want to run in parallel to the main thread. TParallel.For is about performing some actions on the items in a collection in parallel. In both cases you are focussed on the actions you want to perform.

I’ve struck many people however, who struggle to conceptually understand a Future, and when I drill into why, in most cases it’s because they are thinking about Futures in the same way: as being about some code or actions they want to perform. I’ve even had someone say to me “This is about some job I want to have happen in the future, but not now? I don’t get it.”

nofuture-e1350930184618

If you’re in this situation, stop thinking about Futures in the context of the activities you want to do.

A Future is not about a task you want to perform in the future, it’s about a value you know you’ll want in the future.

Let’s say I’m trying to make a decision about whether I can process a customer order:

procedure TFormThreading.OrderItem(const CustomerID,  ItemID: String; OrderQty: Integer);
var
  StockLevel : Integer;
  PaidTheirBills : Boolean;
begin
  StockLevel := GetStockLevel(ItemID); // very slow
  PaidTheirBills := HaveTheyPaid(CustomerID); // also very slow

  if (StockLevel >= OrderQty) and (PaidTheirBills) then
    // process the order
end;

In this scenario, there might be several pieces of information I need before I can go ahead and process the order, such as whether we have enough stock, whether the customer has good enough credit, and potentially others.

Further, let’s assume that the calls to find out our stock levels and also the customer’s credit worthiness are time consuming calls. I could do them sequentially, as above, but then every order will take a long time.

Yes, I could rejig the logic and check the StockLevel straight after retrieving it, exiting if we don’t have enough, but that will still leave me with a slow process in the cases where we do.

But if we step back, we know we need two values to make the decision: StockLevel and PaidTheirBills. Further, the decision will be made in the future, we first have to retrieve the values. So, let’s use a Future.

Let’s rejig the code to look like this:

procedure TFormThreading.OrderItem(const CustomerID, ItemID: String; OrderQty: Integer);
var
  StockLevel : IFuture<Integer>;
  PaidTheirBills : Boolean;
begin
  StockLevel := TTask.Future(function : Integer
                             begin
                               // very slow
                               Result := GetStockLevel(ItemID);
                             end);
  PaidTheirBills := HaveTheyPaid(CustomerID); // also very slow

  if (StockLevel.Value >= OrderQty) and (PaidTheirBills) then
    // process the order
end;

Instead of being an Integer, StockLevel is now an IFuture<Integer>.  It’s not an Integer we need right now, but one we need in the future. We instantiate it by calling TTask.Future, passing in an anonymous method which contains the code needed to calculate its value. This will start running in the background, but in the meantime, we’ll continue on to calculate the other value we need, PaidTheirBills, another time consuming call.

When it comes time to make our decision, a couple of things might have happened.

  • Our anonymous method might have already finished running, in which case calling StockLevel.Value will immediately return the result, which we can then compare to OrderQty.
  • Our anonymous method might still be running, in which case the call to StockLevel.Value will block until it finishes.

But either way, we know that both values will be calculated and compared before we process the order, but it’s likely the user will not have waited as long for the result.

3 Comments

  • Hi Malcolm,

    Moving past Hello World!

    The examples you have given would both use multiple hits to the DataBase to calculate Customer status, Customer Specific Price and availability. How about a fleshed out, thread safe, example using FireDac connections to, say interbase, which executes these SQL requests inside the future threads. Do we share a connection, or would each thread instance use a different DB connection. Show us your stuff Mal!

    Al.

    • Hi Al,

      Given this post was aimed at people who don’t even understand what Futures are, a database example would probably not help. However, if you’re looking for one, watch Danny Wind’s CodeRage session. I think his last example ( or second last) is a database example using TTasks, which covers exactly the same territory you’d need if using Futures. https://www.youtube.com/watch?v=rZfux4by0po

      As an aside, when I was doing this, I was not imagining that both the lookups would be database hits. In my mind, I was imagining things like calling off to other services via REST, as usually that’s where the real latency comes in. Finding the payment history in Xero, for example. So another of Danny’s examples was doing parallel REST calls as well.

      Stay for the Q&A at the end, some good tips in there.

  • […] WTF is a Future? […]

Join the Discussion

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>