An actor model implementation in C# using TPL DataFlow

The actor model (Wikipedia)
An actor is an entity that you can send messages to. In response to a message an actor can do any of the following:
* Send messages to other actors
* Create new actors
* Set behaviour for the next message it receives, (that is, change its internal state)

Lets say you have a concurrent system where many threads want to access the same resource. Instead of protecting the resource with a lock, mutex or semaphore, you create an actor that handles the resource. Then you send messages to the actor and the actor accesses the resource as it processes the messages one by one.

TPL DataFlow
TPL DataFlow (MSDN) is a library for building asynchronous data processing application. It is well suited for actors because it implements, among other things, message passing.

The sample domain
We are a bank and we have accounts. Each account has a balance. We want to be able to deposit money and check the balance.

C# implementation
We want to implement the system using actors. Let’s start with the messages we are going to send to the actors.

Messages
The basic form of a message is ‘something’ sent to an actor. So lets declare it like this:

To deposit money we send a deposit message with an amount.

To check the balance we send a QueryBalance message. The result of the query should be sent as a message to another actor, so we include the receiver in the query message.

The final message is the result of the balance check.

Actors
We implement an actor using an ActionBlock from the DataFlow library. An ActionBlock has an input queue you can post messages to and an action that will be invoked for each received message.

In the constructor of the Actor we create an action that will convert the actor and the message to dynamic objects and then call the Handle method. This means that the runtime will look up a method called ‘Handle’ with a parameter of the message type and call it.

An account actor should have a balance and handle the Deposit and the QueryBalance messages.

We also need an actor that outputs the result of the balance query

The program
Now we can create the actors and send messages to them.

This program first sends a Deposit message to the account actor. The account actor receives it and increased the balance.
The program then sends a QueryBalance message to the account actor. The account actor receives it and sends a Balance message to the output actor who writes the balance to the console.

If you run this program the output is:
Done!
Balance is 50

Notice that “Done!” is before the balance statement! The program sent the messages and finished before the actors were done processing the messages. How do we know when the actors are done processing all messages?
To solve this we use the completion feature of DataFlow and add a Completion property to the Actor class

This tells the ActionBlock to stop receiving messages and return a Task that will complete when all messages currently in the queue have been processed.

We can now modify the program to wait for the actors to finish.

The program now produces this output:
Balance is 50
Done!

Conclusion
TPL DataFlow makes it easy to use actor based programming. This is a very simplistic implementation. It does not handle any errors, like what happens if you pass a message type the actor has no Handle method for, or what happens if an exception is throw during processing of the message. TPL DataFlow has good features to handle exceptions, see http://blogs.msdn.com/b/pfxteam/archive/2011/11/10/10235570.aspx, so you could add error handling rather easy.

11 Comments

  1. Cool!

    Great and simple explanation of the Actor model, which not requires to learn Erlang first.

    Thanks!

  2. Svick

    I don’t like this solution for several reasons:

    1. Borrowing a term you accidentally have in your code, this is a big “dynamic mess”. You can send any message to any actor and if anything is wrong, you’re going to get an exception at runtime (i.e. no compile time checking).

    2. You ask for the balance in one piece of code and then receive it in completely different part of the code. This is why asynchronous programming has been quite difficult and why C# 5.0 introduced async-await. You should take advantage of that.

    3. I think that a property getter that has significant side-effects (like your Completion) is a very bad practice. But this would be easy to fix.

    I think a much better solution for this is some form of AOP: you would write your code as normal (thread-unsafe) methods and then the AOP library would take that code and turn it into (thread-safe) actor-based code using ActionBlock.

    The big advantage of this is that it’s transparent to the user: it looks to him like normal (async) method calls, but the implementation is actually an actor.

    • Hi, Svick!

      1. Yes, my code will crash if you send an message the actor does not implement. I do point this out in my conclusion. Error handling is out of scope for this blog post. Perhaps I will write a follow up post on the subject.

      2. Yes, the query and the result are separated. The account actor has the balance and the output actor writes to screen. They are very loosly coupled, and that is the whole point with actors.

      3. Yes, you are right. The getter should not have side effects. I will change it to a method.

      /Johan

      • Svick

        Sure, decoupling responding to the request and executing the request makes sense. What I was complaining about is that you also decouple making the request and responding to it. I think that does *not* make sense.

        For example, if you wanted to do something like the following code with your actors (while keeping the serial nature of the loop), the code would become very complicated.

        foreach (Account account in accounts)
        {
        Console.WriteLine(“Balance is: {0}”, account.GetBalance());
        }

        • Johan Olsson

          A system of actors is not serial. It is concurrent and non-deterministic. We can write the balance of all accounts with this code:

          foreach (AccountActor actor in accounts)
          {
          actor.Send(new QueryBalance {Receiver = output});
          }

          All actors calculate the balance in parallel and send the result to the output actor. The order of the messages received by the output actor depends on thread scheduling and various other factors. Simply put, we can not know. If we run the code again, the order of the output will probably be different.
          If determinism is important to you, then actors is not the solution you are looking for ;-)

          Regards
          Johan

  3. Hi,
    Nice to see that you’ve also used TPL DataFlow to implement the actor model.
    I’ve recently started to play with this myself:
    http://rogeralsing.com/2014/01/01/pigeon-akka-actors-for-net/

    https://github.com/rogeralsing/Pigeon

    This is pretty much a port of the Java/Scala Actor framework Akka to .NET
    (using the same’ish implementation as you do behind the scenes)

    Let me know if you want to get involved or have any feedback :-)

  4. Patrick H

    Thank you very much. This is the best simple example that I could find for Actor implementation in C#. I am an experienced LabVIEW developer and I have used actors exclusively in tackling applications with many processes. I have recently made the jump into C# in a new position and this really helped me get started implementing actors in my new applications.

  5. Martin

    This is really nice, but what about inter process communication? I think this is the area where actor’s become really useful.

  6. Delaney Gillilan

    Love the idea of using Dataflow & dynamic generic method invocation, however I’m seeing issue as soon as you move the Message & Actor into its own project. Now you get the “X does not contain a definition for ‘Handle’” exception that happens if you don’t have a matching Handle. Any thoughts?

  7. Dragan Radovac

    A clean and simple explanation of the Actor pattern. Very nice. Thanks Johan !

Leave a Reply