如何在Agent之间进行通信?

时间:2014-01-18 09:52:00

标签: concurrency f# functional-programming

使用MailboxProcessor中的F#,他们之间进行通信的首选方式是什么? - 将代理包装到以下对象中:

type ProcessAgent(saveAgent:SaveAgent) = ...
type SaveAgent() = ...

let saveAgent = new SaveAgent()
let processAgent = new ProcessAgent(mySaveAgent)

或者说:

type ProcessAgent(cont:string -> unit) = ...
type SaveAgent() = ...

let saveAgent = new SaveAgent()
let processAgent = new ProcessAgent(fun z -> saveAgent.Add z)

或者甚至可能是:

type ProcessAgent() = ...
type SaveAgent() = ...

let saveAgent = new SaveAgent()
let processAgent = new ProcessAgent()

processAgent.Process item (fun z -> saveAgent.Add z)

还有没有理由将正常的函数包含在代理中,而不是将某种状态包含在代理中?

2 个答案:

答案 0 :(得分:2)

将代理封装在类中的关键是它可以让你打破它们之间的直接依赖关系。因此,您可以创建单个代理,然后通过注册事件处理程序,调用方法等将它们连接到更大的“代理网络”。

代理商基本上可以公开三种成员:

  • 操作'T -> unit类型的成员。他们向代理发送一些消息,而不等待代理的任何回复。这基本上包含了对agent.Post的调用。

  • 阻止操作'T -> Async<'R>类型的成员。当您向代理发送一些消息但后来想要等待响应(或确认已处理该操作)时,这非常有用。这些不会阻塞逻辑线程(它们是异步),但它们阻止了调用者的执行。这基本上包含了对agent.PostAndAsyncReply的调用。

  • 通知IEvent<'T>IObservable<'T>类型的成员,代表代理商报告的某种通知 - 例如当代理完成一些工作并想要通知呼叫者(或其他代理人)时。

在您的示例中,处理代理正在执行某些工作(异步),然后返回结果,因此我认为使用“阻止操作”是有意义的。保存代理的操作只是一个“动作”,因为它不会返回任何内容。为了演示最后一种情况,我将添加“刷新”通知,当保存代理将所有排队项目保存到实际存储时会调用该通知:

// Two-way communication processing a string
type ProcessMessage = 
  PM of string * AsyncReplyChannel<string>

type ProcessAgent() = 
  let agent = MailboxProcessor.Start(fun inbox -> async {
    while true do 
      let! (PM(s, repl)) = inbox.Receive()
      repl.Reply("Echo: " + s) })
  // Takes input, processes it and asynchronously returns the result
  member x.Process(input) =
    agent.PostAndAsyncReply(fun ch -> PM(input, ch))

type SaveAgent() = 
  let flushed = Event<_>()
  let agent = (* ... *)
  // Action to be called to save a new processed value
  member x.Add(res) =
    agent.Post(res)
  // Notification triggered when the cache is flushed
  member x.Flushed = flushed.Publish

然后,您可以使用成员创建两个代理并以各种方式连接它们:

let proc = ProcessAgent()
let save = SaveAgent()

// Process an item and then save the result
async { 
  let! r = proc.Process("Hi")
  save.Save(r) }

// Listen to flushed notifications 
save.Flushed |> Event.add (fun () -> 
  printfn "flushed..." )

答案 1 :(得分:0)

您无需为代理创建类。为什么不写一个返回MailboxProcessor的函数?

let MakeSaveAgent() =
    MailboxProcessor<SaveMessageType>.Start(fun inbox ->
        (* etc... *))

let MakeProcessAgent (saveAgent: MailboxProcessor<SaveMessageType>) =
    MailboxProcessor<ProcessMessageType>.Start(fun inbox ->
        (* etc... you can send messages to saveAgent here *))

对于你的最后一个问题:不,不是真的,当一个返回Async<_>的简单函数就足够时会增加不必要的复杂性。