【问题标题】:Implementing Actor Model in C# 5 / Async CTP在 C# 5 / Async CTP 中实现 Actor 模型
【发布时间】:2011-08-12 05:24:39
【问题描述】:

在演员模型中,演员有某种消息循环,其中消息使用例如匹配。模式匹配(取决于c语言)

例如伪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()

差不多,一个消息签名被匹配并与对消息内容执行的某些操作相关联。

this 和调用实际方法有什么概念上的区别吗? 例如如果我要在 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。

那么这是否足以让它发挥作用?

【问题讨论】:

  • 这种风格的问题在 Code Review 中感觉更自在。这对我的口味来说有点开放。 codereview.stackexchange.com
  • 如果使用 C#,我强烈建议学习 Rx。它非常强大、高效且富有表现力。你应该能够用 Rx 来模拟​​你的问题。我什至有时会推荐 Rx 用于 F#,而不是面向代理的方法。另外,Rx 团队已经涵盖了许多在 C# 中实现时可能出现错误的极端情况。
  • 您提到的缓冲区块是 TPL 数据流的一部分,它本身就是一个参与者模型。您实际上可以使用此模型为 MailboxPProcessor 建模。我写了一系列帖子来演示这一点,您可能会觉得这很有用:moiraesoftware.com/blog/2012/01/22/FSharp-Dataflow-agents-I

标签: f# actor agent async-ctp


【解决方案1】:

基于任务的异步和 MailboxProcessor 之间存在细微差别。邮箱处理器将始终在同一个线程中结束,类似于 Winforms 消息循环。任务保持一个 SynchronizationContext。这意味着 Winforms 和 WPF 的行为相同,但在使用线程池时您可能会使用不同的线程。

否则,从概念上讲,我认为是正确的。

【讨论】:

  • MailboxProcessor 在内部使用 RegisterWaitForSingleObject 发布到线程池,尽管这由内部蹦床支持,该蹦床导致线程每 300 次迭代跳转一次。 MailBox 处理器中的异步可以使用 Async.SwitchToContext 强制到特定线程。
【解决方案2】:

我会说你的方法是合理的。

在接口后面封装一个 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 ()

【讨论】:

    猜你喜欢
    • 2017-07-04
    • 2012-07-11
    • 2012-11-27
    • 1970-01-01
    • 2015-07-16
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2015-02-23
    相关资源
    最近更新 更多