Реализация модели актера в C# 5 / Async CTP
В модели актера у актеров есть своего рода цикл сообщений, где сообщения сопоставляются с использованием, например, сопоставления с образцом (в зависимости от языка ofc)
например, псевдо 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()
Таким образом, подпись сообщения сопоставляется и связана с некоторым действием, выполняемым над содержимым сообщения.
Есть ли концептуальная разница между этим и вызовом реальных методов? например, если бы я сделал следующее в 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 ....
}
Таким образом, вызов метода в MyActor приведет к асинхронному сообщению, размещенному в очереди сообщений, которое обрабатывает только один тип сообщения; действие. Однако поведение, связанное с сообщением, содержится в самом сообщении (опубликованном с помощью открытого метода)
Так будет ли это считаться чистым способом для актеров в C# 5 / Async CTP?
Преимущества будут состоять в том, что сообщения просто определяются как обычные сообщения, а не как классы, похожие на DTO.
Так этого будет достаточно, чтобы заставить это работать?
2 ответа
Существует небольшая разница между асинхронностью на основе задач и MailboxProcessor. Процессор почтовых ящиков всегда будет находиться в одном потоке, как в цикле сообщений Winforms. Задача хранит SynchronizationContext. Это означает одинаковое поведение для Winforms и WPF, но вы можете оказаться в другом потоке при работе с пулом потоков.
В противном случае, и концептуально, выглядит правильно для меня.
Я бы сказал, что ваш подход разумен.
На самом деле хорошей практикой является инкапсуляция агента F# за интерфейсом, который сам отправляет сообщения агенту:
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 ()