Implementing an actor model in C # 5 / Async CTP

In the actorโ€™s model, the actors have a kind of message loop where messages are matched, for example, pattern matching (depending on the language c)

eg. 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() 

To a large extent, the signature of the message is mapped and associated with some actions to execute the message content.

Is there any conceptual difference between this and calling real methods? for example, if I 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 .... } 

Thus, a method call on MyActor will result in an asynchronous message message sent to a message queue that processes only one kind of message; act. However, the behavior associated with the message is contained in the message itself (published from a public method).

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

The benefits would be that messages are simply defined as regular messages, rather than creating fuzzy messages like DTO classes.

So will it be enough to make it work?

+4
source share
2 answers

There is a slight difference between task-based asynchrony and MailboxProcessor. The mailbox processor will always be in the same thread, similar to the Winforms message loop. The task contains a SynchronizationContext. This means the same behavior for Winforms and WPF, but when working with a thread pool, you may end up in a different thread.

Otherwise, and conceptually, it looks right.

0
source

I would say that your approach is reasonable.

In fact, it is good practice to encapsulate an F # agent behind an interface that sends messages to the agent itself:

 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 () 
0
source

All Articles