问题 来自C#的MailboxProcessor


您是否尝试过使用C#的T的MailboxProcessor?你能发贴示例代码吗?

你如何开始一个新的,发布消息,以及如何处理它们?


1936
2018-04-07 13:16


起源



答案:


虽然你可以使用 MailboxProcessor<T> 直接来自C#(使用C# async 在我的另一个答案中指出,这不是一件好事 - 我写的主要是为了好奇。

MailboxProcessor<T> type被设计为从F#中使用,因此它不适合C#编程模型。您可能可以为C#实现类似的API,但它不会那么好(当然不是在C#4.0中)。该 TPL DataFlow库(CTP) 为C#的futrue版本提供了类似的设计。

目前,最好的办法是使用代理实现代理 MailboxProcessor<T> 在F#中,通过使用它使C#使用友好 Task。这样,您可以在F#中实现代理的核心部分(使用尾递归和异步工作流),然后从C#中编写和使用它们。

我知道这可能不会直接回答你的问题,但我认为值得举一个例子 - 因为这是组合F#代理的唯一合理方式(MailboxProcessor)用C#。 我最近写了一个简单的“聊天室”演示,所以这是一个例子:

type internal ChatMessage = 
  | GetContent of AsyncReplyChannel<string>
  | SendMessage of string

type ChatRoom() = 
  let agent = Agent.Start(fun agent -> 
    let rec loop messages = async {
      // Pick next message from the mailbox
      let! msg = agent.Receive()
      match msg with 
      | SendMessage msg -> 
          // Add message to the list & continue
          let msg = XElement(XName.Get("li"), msg)
          return! loop (msg :: messages)

      | GetContent reply -> 
          // Generate HTML with messages
          let html = XElement(XName.Get("ul"), messages)
          // Send it back as the reply
          reply.Reply(html.ToString())
          return! loop messages }
    loop [] )
  member x.SendMessage(msg) = agent.Post(SendMessage msg)
  member x.AsyncGetContent() = agent.PostAndAsyncReply(GetContent) 
  member x.GetContent() = agent.PostAndReply(GetContent)

到目前为止,这只是一个标准的F#代理。现在,有趣的位是暴露的以下两种方法 GetContent 作为可从C#使用的异步方法。该方法返回 Task 对象,可以从C#的常规方式使用:

  member x.GetContentAsync() = 
    Async.StartAsTask(agent.PostAndAsyncReply(GetContent))

  member x.GetContentAsync(cancellationToken) = 
    Async.StartAsTask
     ( agent.PostAndAsyncReply(GetContent), 
       cancellationToken = cancellationToken )

这将在C#4.0中合理使用(使用标准方法,如 Task.WaitAll 当你能够使用C#时,它将在下一版本的C#中更加出色 await 用于处理任务的关键字。


16
2018-04-07 20:45



谢谢你的详细解答。我会试一试!缺少一件 - 来自C#4.0的实际用途。 - GregC


答案:


虽然你可以使用 MailboxProcessor<T> 直接来自C#(使用C# async 在我的另一个答案中指出,这不是一件好事 - 我写的主要是为了好奇。

MailboxProcessor<T> type被设计为从F#中使用,因此它不适合C#编程模型。您可能可以为C#实现类似的API,但它不会那么好(当然不是在C#4.0中)。该 TPL DataFlow库(CTP) 为C#的futrue版本提供了类似的设计。

目前,最好的办法是使用代理实现代理 MailboxProcessor<T> 在F#中,通过使用它使C#使用友好 Task。这样,您可以在F#中实现代理的核心部分(使用尾递归和异步工作流),然后从C#中编写和使用它们。

我知道这可能不会直接回答你的问题,但我认为值得举一个例子 - 因为这是组合F#代理的唯一合理方式(MailboxProcessor)用C#。 我最近写了一个简单的“聊天室”演示,所以这是一个例子:

type internal ChatMessage = 
  | GetContent of AsyncReplyChannel<string>
  | SendMessage of string

type ChatRoom() = 
  let agent = Agent.Start(fun agent -> 
    let rec loop messages = async {
      // Pick next message from the mailbox
      let! msg = agent.Receive()
      match msg with 
      | SendMessage msg -> 
          // Add message to the list & continue
          let msg = XElement(XName.Get("li"), msg)
          return! loop (msg :: messages)

      | GetContent reply -> 
          // Generate HTML with messages
          let html = XElement(XName.Get("ul"), messages)
          // Send it back as the reply
          reply.Reply(html.ToString())
          return! loop messages }
    loop [] )
  member x.SendMessage(msg) = agent.Post(SendMessage msg)
  member x.AsyncGetContent() = agent.PostAndAsyncReply(GetContent) 
  member x.GetContent() = agent.PostAndReply(GetContent)

到目前为止,这只是一个标准的F#代理。现在,有趣的位是暴露的以下两种方法 GetContent 作为可从C#使用的异步方法。该方法返回 Task 对象,可以从C#的常规方式使用:

  member x.GetContentAsync() = 
    Async.StartAsTask(agent.PostAndAsyncReply(GetContent))

  member x.GetContentAsync(cancellationToken) = 
    Async.StartAsTask
     ( agent.PostAndAsyncReply(GetContent), 
       cancellationToken = cancellationToken )

这将在C#4.0中合理使用(使用标准方法,如 Task.WaitAll 当你能够使用C#时,它将在下一版本的C#中更加出色 await 用于处理任务的关键字。


16
2018-04-07 20:45



谢谢你的详细解答。我会试一试!缺少一件 - 来自C#4.0的实际用途。 - GregC


此解决方案需要C#“异步CTP”,但请查看 C#中的Agent / MailboxProcessor使用新的async / await


0
2018-04-07 13:21



问题是专门针对C#4.0,因为我双手绑在一起。为了这个,我会在心跳中使用F#,但我还不能。 Async CTP是否有上线许可证? - GregC
正如Tomas P.指出的那样,这个问题的关键在于尾递归调用导致StackOverflowException。我正在寻找替代品。 - GregC
@GregC,不,它没有上线许可证。它只是一个预览,顾名思义,你不应该在生产中使用它 - Thomas Levesque
很难忘掉强大的编程范式。一旦你知道它,你拒绝用旧的简单方式实现。 - GregC