4

In an Actor Model, the actors have some sort of message loop where messages are matched using e.g. pattern matching (depending on language ofc)

e.g. pseudo F#

 let message_loop() = 
     let! message = receive_message() //sync receive message
     match message with
     | Foo(a,b) -> DoStuff(a,b)
     | Bar(c) -> DoOtherStuff(c)
     | _ -> error ("unknown message")
     message_loop()

So pretty much, a message signature is matched and associated with some action to perform on the message content.

Is there any conceptual difference between this and calling actual methods? e.g. if I would do the following in C# 5:

class MyActor 
{
   //message signature Foo(a,b)
   public void Foo(int a,string b)
   {
      Act( () => DoStuff(a,b) );
   }

   //message signature Bar(c)
   public void Bar(int c)
   {
      Act( () => DoOtherStuff(c));
   }

   // the rest is infrasturcture code, can be refactored into an Actor Base class


   //this emulates the sync behavior of the above actor
   //each action is pushed onto a queue 
   //and then processed synchronously by the message handler
   private void Act(Action action)
   {
       actions.Post(action); 
   }

   private BufferBlock<Action> actions = new BufferBlock<Action>();

   //this needs max degreee of parallellism = 1 
   private ActionBlock<Action> messageHandler = new ....
}

This way, invoking a method on MyActor will result in an async message posted onto a message queue which only handles a single kind of message; an Action. However, the behavor associated with the message is contained in the message itself (posted from the public method)

So would this be a considered a clean way to do actors in C# 5 / Async CTP?

The benefits would be that messages are simply defined as normal messages instead of creating awkward message DTO like classes.

So would this be enough to make it work?

Jonas
  • 121,568
  • 97
  • 310
  • 388
Roger Johansson
  • 22,764
  • 18
  • 97
  • 193
  • 2
    this style of question feels more at home at Code Review. It's a bit open-ended for my tastes. http://codereview.stackexchange.com/ – GregC May 17 '11 at 13:18
  • Possibly related [Agent/MailboxProcessor in C# using new async/await](http://stackoverflow.com/questions/4075189/agent-mailboxprocessor-in-c-sharp-using-new-async-await) – Benjol Nov 17 '11 at 06:41
  • 1
    If using C#, I would strongly recommend learning Rx instead. It's very powerful, efficient and expressive. You should be able to model your problem with Rx. I would even sometimes recommend Rx for F# over agent-oriented approaches. Plus, lots of corner case which could be buggy when implemented yourself in C# will have been covered by the Rx team. – David Grenier Nov 18 '11 at 15:48
  • The bufferblock you mention are part of TPL Dataflow which is itself an actor model. You can actually model the MailboxPRocessor with this. I wrote a series of post demonstrating this which you may find useful: http://moiraesoftware.com/blog/2012/01/22/FSharp-Dataflow-agents-I/ – 7sharp9 Aug 17 '12 at 09:23

2 Answers2

1

There is a slight difference between Task-based asynchrony and MailboxProcessor. Mailbox processor will always end up in the same thread, similar to Winforms message loop. Task keeps a SynchronizationContext. This means the same behavior for Winforms and WPF, but you could end up on a different thread when working with a thread-pool.

Otherwise, and conceptually, looks right to me.

GregC
  • 7,737
  • 2
  • 53
  • 67
  • 1
    The MailboxProcessor internally posts to the threadpool using RegisterWaitForSingleObject although this is backed by an internal trampoline that causes the thread to jump every 300 iterations. The Async within the MailBox processor can be coerce to a specific thread using Async.SwitchToContext. – 7sharp9 Aug 17 '12 at 09:20
0

I would say your approach is reasonable.

It is actually a good practice to encapsulate an F# agent behind an interface, itself dispatching messages to the agent:

type IPrintText =
    abstract Stop : unit -> unit
    abstract Print : string -> unit

module Printer =
    type private Message =
        | PrintText of string
        | Stop

    let Start () =
        let agent =
            MailboxProcessor.Start (fun inbox ->
                    let rec loop () = async {
                            let! msg = inbox.Receive()

                            return!
                                match msg with
                                | PrintText text ->
                                    printfn "%s" text
                                    loop ()
                                | Stop -> async.Zero()
                        }
                    loop ())

        { new IPrintText with
            member x.Stop () = agent.Post Stop
            member x.Print text = agent.Post <| PrintText text }

let agent = Printer.Start ()

agent.Print "Test"
agent.Stop ()
David Grenier
  • 1,098
  • 6
  • 17