Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell concurrency - is forkIO really nondeterministic?

I'm trying to use concurrency in Haskell for a specific optimization in which only one of two values is required, and depending on the situation, either one may be much faster to create than the other.

I thought I could just run 2 threads with forkIO, and then wait until a value is placed in an MVar. Here is a simple Test i have written for this:

import Control.Concurrent

main = do out <- newEmptyMVar
          t1 <- forkIO (makeString out)
          t2 <- forkIO (makeInt out)
          v <- takeMVar out
          killThread t1
          killThread t2
          case v of
               Left s -> putStrLn s
               Right i -> putStrLn $ show i

makeString out = do s <- return ( show (primes !! 10000))
                    putMVar out $ Left s

makeInt out = do i <- return 2
                 putMVar out $ Right i

primes = sieve [2..] 
 where sieve (x:xs) = x : (sieve $ filter ((/=0).(flip mod x)) xs)

Compiled with:

ghc --make -threaded Test

However, only the Left s case is ever reached, although getting the prime should take long enough for the makeInt thread to start (and return 2 really shouldn't take that much time). Why is that, and how do I fix this?

like image 242
Cubic Avatar asked Apr 20 '12 13:04

Cubic


People also ask

How does Haskell handle concurrency?

Concurrency means implementing a program by using multiple I/O-performing threads. While a concurrent Haskell program can run on a parallel machine, the primary goal of using concurrency is not to gain performance, but rather because that is the simplest and most direct way to write the program.

Does Haskell have concurrency?

Concurrent Haskell extends Haskell 98 with explicit concurrency. Its two main underlying concepts are: A primitive type MVar α implementing a bounded/single-place asynchronous channel, which is either empty or holds a value of type α . The ability to spawn a concurrent thread via the forkIO primitive.


1 Answers

The problem here is laziness. The makeString is just inserting a thunk to calculate show (primes !! 10000), which then gets evaluated by the main thread afterwards. Inserting the thunk is quite fast, so it happens to win the race in this case.

To force evaluation to happen within the thread, you can change return to evaluate:

makeString out = do s <- evaluate $ show (primes !! 10000)
                    putMVar out $ Left s

This should cause makeInt to win the race in most cases (although it's not guaranteed).

like image 115
hammar Avatar answered Oct 07 '22 12:10

hammar