2011-04-25 6 views
4

Dans un modèle d'acteur, les acteurs ont une sorte de boucle de message où les messages sont appariés en utilisant, par ex. correspondance de motif (en fonction du langage ofc)Implémentation d'un modèle d'acteur dans CTP 5/Async CTP

par exemple. 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() 

donc à peu près, une signature de message est adapté et associé à une action à effectuer sur le contenu du message.

Y a-t-il une différence conceptuelle entre cette méthode et l'appel de méthodes réelles? par exemple. si je ferais ce qui suit dans 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 .... 
} 

De cette façon, l'invocation d'une méthode sur MyActor se traduira par un message affiché sur async une file d'attente de messages qui gère un seul type de message; une action. Cependant, le comportement associé au message est contenu dans le message lui-même (affiché à partir de la méthode publique)

Alors serait-ce considéré comme une façon propre de faire des acteurs dans C# 5/Async CTP? Les avantages seraient que les messages sont simplement définis comme des messages normaux au lieu de créer des messages de type DTO gênants.

Alors serait-ce suffisant pour le faire fonctionner?

+2

ce style de question se sent plus à l'aise à la révision du Code. C'est un peu ouvert à mes goûts. http://codereview.stackexchange.com/ – GregC

+0

Peut-être lié [Agent/MailboxProcessor en C# en utilisant new async/await] (http://stackoverflow.com/questions/4075189/agent-mailboxprocessor-in-c-sharp-using- new-async-await) – Benjol

+1

Si j'utilise C#, je recommande fortement d'apprendre Rx à la place. C'est très puissant, efficace et expressif. Vous devriez pouvoir modéliser votre problème avec Rx. Je recommande même parfois Rx pour F # sur les approches orientées agents. De plus, beaucoup de cas de coin qui pourraient être bogués lors de l'implémentation en C# auront été couverts par l'équipe Rx. –

Répondre

0

Il existe une légère différence entre l'asynchronisme basé sur une tâche et MailboxProcessor. Le processeur de boîte aux lettres finira toujours dans le même thread, similaire à la boucle de messages Winforms. La tâche conserve un SynchronizationContext. Cela signifie le même comportement pour Winforms et WPF, mais vous pourriez vous retrouver sur un thread différent lorsque vous travaillez avec un pool de threads. Sinon, et conceptuellement, me semble juste.

+1

Le MailboxProcessor publie en interne au pool de threads en utilisant RegisterWaitForSingleObject bien que cette est soutenu par un trampoline interne qui fait sauter le fil toutes les 300 itérations. L'Async dans le processeur MailBox peut être contraint à un thread spécifique en utilisant Async.SwitchToContext. – 7sharp9

0

Je dirais que votre approche est raisonnable.

Il est en fait une bonne pratique pour encapsuler un agent F # derrière une interface, les messages dispatching lui-même à l'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()