Sohail Si
Sohail Si

Reputation: 2986

Synchronising parallel processes in Haskell

In Haskell, how can I run multiple (monad?) functions (such as print) in parallel and see their output in the order of their finish time? I want three processes each one ending up in a print function.

import Control.Parallel
main = a `par` b `pseq` (a,b)
    where
        a = print("ack",ack 3 10)
        b = print("fac",fac 42)

If I don't use pseq, it will show the last one specified in par combination. I want to make sure all processes are finished before the program ends. I tried this but it does not show the output of a,b:

...
main = a `par` b `pseq` print("done.")
...

Note: my program ends with the following lines:

fac 0 = 1
fac n = n * fac (n-1)
ack 0 n = n+1
ack m 0 = ack (m-1) 1
ack m n = ack (m-1) (ack m (n-1))

Upvotes: 0

Views: 440

Answers (2)

Jeremy List
Jeremy List

Reputation: 1766

In the C family of languages, there's a difference between these 2 lines

a = foobar; // This just sets a to be a function pointer to foobar
b = foobar(); // This actually runs foobar and stores the result in `b`

Your code basically has the same problem as if you were writing C and forgot the () at the end of all your function calls. It assigns the function pointers a and b in parallel but doesn't actually call them.

If you are writing pure functions in Haskell, you can add parallelism using par, but it doesn't work for IO because IO a is like a function pointer. Your code "evaluates" a and b in parallel but the result of that evaluation is still waiting for you to actually execute it.

For code that lives in IO, you have to import Control.Concurrent and use the function forkIO to create the new thread. Then because the child threads all automatically die when the main thread finishes, you need some way of making the main thread wait (I'll use MVars in the example because they're the simplest reliable way to do it)

import Control.Concurrent
import Control.Concurrent.MVar

main = do
  -- MVars have a type parameter because you can also use them to send data
  -- between threads, but in this example I'm basically just using them as locks
  await <- newEmptyMVar :: IO (MVar ())
  bwait <- newEmptyMVar :: IO (MVar ())
  forkIO $ print ("ack",ack 3 10) >> putMVar await ()
  forkIO $ print ("fac",fac 42) >> putMVar bwait ()
  takeMVar await
  takeMVar bwait

Upvotes: 1

Fraser
Fraser

Reputation: 1521

Don't use Control.Parallel for running IO actions concurrently.

Control.Concurrent.Async should do what you want – Control.Parallel is used for hinting which pure operations can be evaluated simultaneously, not for running multiple actions (monadic or otherwise) at the same time.

Upvotes: 6

Related Questions