MailboxProcessor<T> from C#
Asked Answered
U

2

13

Have you tried to use a MailboxProcessor of T from C#? Could you post sample code?

How do you start a new one, post messages to it, and how do you process them?

Unseam answered 7/4, 2011 at 13:16 Comment(0)
G
18

While you can use MailboxProcessor<T> directly from C# (using the C# async extension) as pointed out in my other answer, this isn't really a good thing to do - I wrote that mainly for curiosity.

The MailboxProcessor<T> type was designed to be used from F#, so it doesn't fit well with the C# programming model. You could probably implement similar API for C#, but it wouldn't be that nice (certainly not in C# 4.0). The TPL DataFlow library (CTP) provides similar design for the futrue version of C#.

Currently, the best thing to do is to implement the agent using MailboxProcessor<T> in F# and make it friendly to C# usage by using Task. This way, you can implement the core parts of agents in F# (using tail-recursion and async workflows) and then compose & use them from C#.

I know this may not directly answer your question, but I think it's worth giving an example - because this is really the only reasonable way to combine F# agents (MailboxProcessor) with C#. I wrote a simple "chat room" demo recently, so here is an example:

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)

So far, this is just a standard F# agent. Now, the interesting bits are the following two methods that expose GetContent as an asynchronous method usable from C#. The method returns Task object, which can be used in the usual way from C#:

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

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

This will be reasonably usable from C# 4.0 (using the standard methods such as Task.WaitAll etc.) and it will be even nicer in the next version of C# when you'll be able to use the C# await keyword to work with tasks.

Groan answered 7/4, 2011 at 20:45 Comment(1)
Thank you for your detailed answer. I'll give it a shot! One piece is missing -- actual usage from C# 4.0.Unseam
M
0

This solution requires the C# "async CTP" but take a look at Agent/MailboxProcessor in C# using new async/await

Minded answered 7/4, 2011 at 13:21 Comment(4)
Question is specifically geared towards C# 4.0, since I have my hands tied. I'd use F# in a heart-beat for this, but I can't just yet. Does Async CTP have a go-live license?Unseam
As Tomas P. points out, the crux of this problem would be in the tail-recursive call causing a StackOverflowException. I am looking for alternatives.Unseam
@GregC, no it doesn't have a go-live license. It's only a preview, as the name implies, you shouldn't use it in productionNofretete
it's hard to unlearn a powerful programming paradigm. Once you know it, you refuse to implement things the old simpleton way.Unseam

© 2022 - 2024 — McMap. All rights reserved.