Active Objects and Futures

Herb Sutter gave one of my favorite and inspiring presentations.  It is called "The Free Lunch is Over".  The original article can be found here.  My first encounter though came from his PDC presentation and highly recommend viewing that as well.

The part that interested me the most about the talk was two new threading abstractions I hadn't encountered before.  Future's and ActiveObjects.  One of the basic premise is that concurrency should be grep`able and somewhat declarative.  The act of calling a method on a background thread and later waiting for it to complete should be simple, not complicated. 

Asynchronous programming is one of my favorite aspects of computing.  What interests me the most is how asynchronous programming can be useful for UI.  My biggest pet peeve is when UI hangs because an operation call, or network operation takes too long.  Why not make multi-threading easy and give users a way to cancel out of these operations???  Or start loading on the background thread instead of waiting for the user to perform a specific.  Hopefully over the next month or so I'll lay out some utilities and classes building on Futures and Active Objects that will do precisely this.

Future's are actions where work can be done now, but the result is not needed until a future time.  Work occurs on a separate thread and the results can be easily joined once work is complete.

            var f = Future.Create(() => LongCalculation());
            // ...
            var result = f.Wait();

Future's are now exposed via the Parallel Extension team's work.  You can download the CTP off of their web site and get to work.

ActiveObjects are objects which only expose Asynchronous functions where the return value is exposed as a Future.  So instead of

        string GetName()

You would have

        Future<string> GetName()

An ActiveObject essentially lives on or owns a thread.  All operations are queued up and processed one at a time.  Since only one action at a time can be executing the object internals don't have to use locks or consider many types of race conditions.  In fact if your return types are immutable a great many threading concerns go out the window.  Yet all of the calls are inherently asynchronous so callers can get the result only when they are needed.  The best of both worlds. 

Both of these provide significant advantages over the "lock before use" patterns.  In my experience I find these to be hard to maintain and lead to difficult to track down bugs.  I can't tell you how many times I've gone through someone else's code, or even my own, and wondered ...

  • Did they forget to lock here or is this an optimization?
  • Is a join needed here or can these terminate at separate times?
  • OK I need to touch that variable, can it be accessed in multiple threads or is it safe?

Futures/Active Objects on the other hand are a bit more declarative and straight forward to understand than plain old locks.  They allow you to do away with many uses of plain old locking.  Don't confuse this with me saying they are a cure all for threading.  They're not.  But in my experiences I've found them to be a significant upgrade. 

Over the next month or so I'll be laying out the design for a basic ActiveObject implementation.  We will likely have to deviate off of the Parrallel Extension work to get certain behaviors but the concepts map well.

Comments (5)
  1. What do you think of generalizing from Active Objects, to a more scale-out friendly approach like explicit message passing (a la MPI) ?

    In other words, the same model of development holds for both large-scale, distributed systems development, and multi-core development. Instead of using a separate host process for each, we host them all in the same process. Instead of using an inter-process / inter-machine technology like MSMQ or SSSB, we use an in-memory queue.

    I’ve been using nServiceBus to provide this sort of scale-independent API and customize the underlying transport as needed.

    By sharing nothing, it becomes automatically thread safe. When we want to scale-out a certain heavy calculation from the client, we can easily swap in an appropriate implementation without changing any application code.

    I don’t use GetFirstName messages though – they’re usually a lot bulkier. Get all the information for a user, the groups they belong too, etc – a whole graph. The GUI can then bind to whichever part they need.

    This also makes the issue of writing occasionally connected/disconnected clients become solved by default. If everything is just messaging, and we can store and forward those, connected and disconnected are the same programming model. Just the time between sending a message, and getting a message back is different.


  2. I’ve been busy lately and neglected my series on Active Objects .&#160; It’s been a fairly busy time

  3. I’ve been busy lately and neglected my series on Active Objects .&#160; It’s been a fairly busy time

  4. ActiveObjectWith the basic PipeSingleReader class, we now have the last piece necessary to create an…

Comments are closed.

Skip to main content