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.

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.

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:
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

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, so you could add error handling rather easy.

This Post Has 15 Comments

  1. Cool!

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


  2. 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.

    1. 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.


      1. 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());

        1. 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 ;-)


  3. 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.

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

  5. 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?

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

  7. Hi Johan. Your example is nice, clean and clear. I’m just getting started with this. I want to build a workflow composed of several blocks. I want the workflow to be processed asynchronously. The workflow is hidden behind an API. Currently the caller receives the “Completion” property of the last processing block in the workflow. The idea is that the caller can deal with the resulting task in standard fashion (e.g. they may want to call “Wait” or setup a continuation, etc). However I’m not sure what the best way to trigger the completion on that last task. At the moment various steps in the workflow are executed by the final task never completes. What does “Complete()” really mean any way? In my case I want a new instance of the workflow whenever the caller needs some data processed so my workflow just needs to process one data/work item from beginning to end. If I complete the final task explicitly (i.e. calling “Complete”) it short-circuits the workflow (i.e. the steps are not executed). I tried to use DataflowLinkOptions.PropagateCompletion as part of the linking but that didn’t work either. If I can’t work this out I would rather roll out my own implementation using the standard TPL or something else. :-(

    1. For example, If you set up the dataflow to read input from a continous data stream, like a videocamera, then Complete() stops the dataflow. No more data is read from the stream and no more data is output from the dataflow.

      In your case, if I understand this correctly, I would not use the Complete() method at all. I would simply build the dataflow chain, send the data to the first step and then receive the data in the last step. Like this:

      public async Task ProcessData(int input)
      var firstBlock = new TransformBlock
      (data => data + 1);
      var lastBlock = new TransformBlock
      (data => data * 2);

      await firstBlock.SendAsync(input);
      return await lastBlock.ReceiveAsync();

  8. I have project where I scrape many e-commerce sites to track price and inventory changes and update prices and inventory changes into database.
    Due to application running into single node. Its slow to scrape , I am also using thread and async model but still its limitation due to single node and some time its throwing db concurrency issue. So, I am planning to move scraping and db update module into Akka or strom.
    Can you suggest that which one best for such type case ?

  9. that is really awesome explain for actor model


Leave a Reply

Close Menu