views:

775

answers:

2

I noticed the following comment in my copy of Expert F# on page 379:

Passing and Processing Messages

A distinction is often made between shared-memory concurrency and message passing concurrency. The former is often more efficient on local machines and is covered in the section "Using Shared-Memory Concurrency" later in this chapter. The latter scales to systems where there is no shared memory, for example, distributed systems, and can also be used to avoid performance problems associated with shared memory.

I'm interested message passing concurrency between processes with no shared memory. All of the examples in Expert F# and on the internet which demonstrate how to use the MailboxProcessor contain some variation of this code:

let counter =
    MailboxProcessor.Start(fun inbox ->
        let rec loop n =
            async {
                do printfn "n = %d, waiting... " n
                let! msg = inbox.Receive()
                match msg with
                    | -1 ->
                        do printfn "'Til the bitter end..."
                        return ()
                    | n -> return! loop(n + msg)
            }
        loop 0)

counter.Post(20)
counter.Post(50)
counter.Post(-1) // kill mailbox

In other words, you have to have a handle on your MailboxProcessor in shared-memory before you can post messages to its channel. This isn't Erlang-style concurrency as far as I know it, since you can only post messages to MailboxProcessors in the same process (note: process, not thread).

Is it possible for one MailboxProcessor in one process to send messages to another MailboxProcessor process? If so, could you provide a sample?

+5  A: 

I think you've been a little confused by terminology. Erlang processes do not necessarily correspond directly to OS processes. A given OS process can have multiple Erlang processes (and usually does), much like your process has multiple threads. If you want to communicate between multiple OS processes, you may want to check out System.Runtime.Remoting.Channels.Ipc. Conceivably a MailboxProcessor-style wrapper could be created around these APIs.

Logan Capaldo
+3  A: 

The MailboxProcessor and AsyncReplyChannel do not provide the same location transparency as the "pid bang" (Pid !) operation in Erlang. Of course, this only works when the distributed Erlang nodes are configured properly, i.e. names, DNS, synchronized modules, cookies, etc. There are some features in OTP to make this administration easier. Of course, if the Erlang processes are on the same node, it just works. But, there are some wrinkles with distributed Erlang.

"The network is secure." The built-in distributed Erlang mechanisms assume the network is secure. So, a socket-based communication approach with proxy Erlang processes is employed when security is necessary.

"The network is reliable." One of the things that make distributed Erlang work is its philosophy of error handling, namely that processes are unreliable and thus fault tolerance is only achieved by communicating process monitors. OTP codifies patterns (i.e. Supervisor) to implement this philosophy. Reliable messaging in Erlang can be achieved via Mnesia (a distributed database), as was done in RabbitMQ, but you don't get it out of the box.

In the end, distributed communications are never so easy. We could implement an AsynchWorker in F# to act as our proxy and communicate with it via AsynchReplyChannel.Send. We still have to think about the fallacies of distributed computing.

Finally, message passing style concurrency does not imply out-of-process communication. It does imply that there is no shared state to manage with locks, thus a simpler, less error prone model of parallel computation. I think this Prime Number Sieve is a great example of this style of concurrency. The F# example is not as aesthetically pleasing as the Squeak or an Erlang implementation, because of the lack of built-in syntax for message passing, but it works.

Christopher Atkins