Реализация модели актера в C # 5 / Async CTP - PullRequest
4 голосов
/ 25 апреля 2011

В модели актера у актеров есть своего рода цикл сообщений, в котором сообщения сопоставляются с использованием, например, сопоставления с образцом (в зависимости от языка 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 ]

0 голосов
/ 18 ноября 2011

Я бы сказал, что ваш подход разумен.

Хорошей практикой является инкапсуляция агента 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 ()
0 голосов
/ 17 мая 2011

Существует небольшая разница между асинхронностью на основе задач и MailboxProcessor.Процессор почтовых ящиков всегда будет находиться в одном потоке, как в цикле сообщений Winforms.Задача хранит SynchronizationContext.Это означает то же самое поведение для Winforms и WPF, но вы могли бы оказаться в другом потоке при работе с пулом потоков.

В противном случае, и концептуально, выглядит мне правильным.

...