【问题标题】:Generic reply from agent/mailboxprocessor?来自代理/邮箱处理器的一般回复?
【发布时间】:2015-06-29 05:34:21
【问题描述】:

我目前有一个代理通过不断向自己发布“工作”消息来进行大量数据处理。

有时,此代理的客户端希望中断此处理以安全地访问数据。

为此,我认为向代理发布异步消息以便代理在安全状态下可以运行会很好。这工作正常,消息如下所示:

type Message = |Sync of Async<unit>*AsyncReplyChannel<unit> 

而代理处理就变成了:

match mailbox.Receive () with
| Sync (async, reply) -> async |> Async.RunSynchronously |> reply.Reply

只要客户端不需要从异步返回一些值,这很有效,因为我已将异步/回复限制为单元类型,并且我不能在可区分联合中使用泛型类型。

解决此问题的最佳尝试涉及到包装器异步和等待句柄,但这看起来很混乱,而且不像我对 F# 所期望的那样优雅。我也是 F# 中异步工作流的新手,所以我很可能在这里错过/误解了一些概念。

所以问题是;如何在代理响应中返回泛型类型?

【问题讨论】:

  • 当然你可以在消息中使用泛型:type Message&lt;'a&gt; = Sync of ...*AsyncReplyChannel&lt;'a&gt; 你可能只需要冒泡这一切......
  • 顺便说一句:当你只是用RunSynchronously 运行它时,为什么要使用Async&lt;unit&gt; - 在这种情况下,一个简单的() -&gt; () 也可以(或者如果你想要它通用:@ 987654327@
  • 另一件事:您使用的是开箱即​​用的MailboxProcessor吗?因为.Receive 应该是异步的(所以它不应该与match 一起使用)——在这种情况下,传递异步工作流是有意义的
  • 嗯,我发誓我尝试在联合中使用泛型参数,我猜这解决了它(但是当它刚刚使用时,它与 Message 定义中定义的类型参数看起来有点混乱我有许多案例中的一个)。
  • 关于异步 vs ()->(),好吧,不是真的,我仍然在搞乱我的架构,我的想法是,因为这取决于代理应该如何以及何时执行任务运行并且所有客户端都知道“它将在未来某个时间在其他线程上运行”,我认为异步将是一个不错的语义选择。它还使客户端可以轻松切换同步上下文以及更新 ui。

标签: asynchronous f# agent


【解决方案1】:

让这变得困难的是,在您当前的版本中,代理必须以某种方式计算该值,然后将其传递给通道,而不知道该值的类型是什么。在 F# 中以静态类型的方式执行此操作很棘手。

如果您将消息设为通用,那么它将起作用,但代理将只能处理一种类型的消息(Message&lt;T&gt; 中的类型T)。

另一种方法是简单地将Async&lt;unit&gt; 传递给代理,让调用者为每个特定类型传递值。所以,你可以这样写消息和代理:

type Message = | Sync of Async<unit>

let agent = MailboxProcessor.Start(fun inbox -> async {
  while true do
    let! msg = inbox.Receive ()
    match msg with
    | Sync (work) -> do! work })

当您使用PostAndReply 时,您可以访问回复通道 - 而不是将通道传递给代理,您可以在本地 async 块中使用它:

let num = agent.PostAndReply(fun chan -> Sync(async { 
  let ret = 42 
  chan.Reply(ret) }))

let str = agent.PostAndReply(fun chan -> Sync(async { 
  let ret = "hi"
  chan.Reply(ret) }))

【讨论】:

  • 啊,这样使用回复频道真是太聪明了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-03
  • 1970-01-01
  • 2020-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多