F# 在C#5/Async CTP中实现参与者模型

F# 在C#5/Async CTP中实现参与者模型,f#,actor,agent,async-ctp,F#,Actor,Agent,Async Ctp,在参与者模型中,参与者具有某种消息循环,其中消息通过模式匹配(取决于ofc语言)进行匹配 e、 g.伪F# 因此,消息签名与要对消息内容执行的某些操作相匹配并关联 这和调用实际方法之间有概念上的区别吗? e、 g.如果我在C#5中做以下操作: 类MyActor { //消息签名Foo(a,b) 公共void Foo(int a,string b) { Act(()=>DoStuff(a,b)); } //消息签名栏(c) 公共空白栏(INTC) { 法案(()=>DoOtherStuff(c))

在参与者模型中,参与者具有某种消息循环,其中消息通过模式匹配(取决于ofc语言)进行匹配

e、 g.伪F#

因此,消息签名与要对消息内容执行的某些操作相匹配并关联

这和调用实际方法之间有概念上的区别吗? e、 g.如果我在C#5中做以下操作:

类MyActor
{
//消息签名Foo(a,b)
公共void Foo(int a,string b)
{
Act(()=>DoStuff(a,b));
}
//消息签名栏(c)
公共空白栏(INTC)
{
法案(()=>DoOtherStuff(c));
}
//其余的是基础结构代码,可以重构为Actor基类
//这将模拟上述参与者的同步行为
//每个操作都被推送到一个队列中
//然后由消息处理程序同步处理
私人无效法案(行动)
{
行动后(行动);
}
私有缓冲块动作=新的缓冲块();
//这需要最大并行度=1
私有ActionBlock messageHandler=新建。。。。
}
这样,调用MyActor上的方法将导致异步消息发布到消息队列上,该消息队列只处理单一类型的消息;行动。 但是,与消息关联的行为包含在消息本身中(从public方法发布)

那么,这是否被认为是在C#5/Async CTP中执行角色的一种干净方法

这样做的好处是,消息被简单地定义为普通消息,而不是创建笨拙的消息DTO类


那么,这是否足以让它工作起来呢?

基于任务的异步和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 ()
键入IPrintText=
摘要停止:单元->单元
摘要打印:字符串->单位
模块打印机=
键入私人消息=
|字符串的打印文本
|停止
让我们开始()=
出租代理人=
MailboxProcessor.Start(有趣的收件箱->
让rec循环()=异步{
let!msg=inbox.Receive()
回来!
配味精
|PrintText文本->
printfn“%s”文本
循环()
|停止->异步.Zero()
}
循环())
{新IPrintText带有
成员x.Stop()=代理.Post-Stop

成员x.Print text=agent。在代码评审时,发布这种类型的问题会让我感觉更自在。对我的口味来说,这有点开放。如果使用C#,可能相关,我强烈建议改为学习Rx。它非常强大、高效和表达力强。你应该能够用Rx模拟你的问题。我甚至有时会推荐Rx用于F#over面向代理的方法。另外,Rx团队已经介绍了很多在C#中实现时可能会出现错误的情况。您提到的缓冲块是TPL数据流的一部分,它本身就是一个参与者模型。您可以用它来实际建模MailboxPRocessor。我写了一系列文章来演示这一点,您可以ind有用:MailboxProcessor使用RegisterWaitForSingleObject在内部发布到线程池,尽管这是由内部蹦床支持的,该蹦床会导致线程每300次迭代跳转一次。邮箱处理器中的异步可以使用Async.SwitchToContext强制到特定线程。
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 ....
}
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 ()