CodeToLive

Concurrent Haskell

Haskell provides powerful abstractions for concurrent and parallel programming, including lightweight threads, software transactional memory (STM), and parallel evaluation strategies.

Lightweight Threads

Haskell's runtime provides extremely lightweight threads:


import Control.Concurrent

-- Simple thread example
main :: IO ()
main = do
  forkIO $ putStrLn "Hello from thread!"
  putStrLn "Hello from main thread"
  threadDelay 1000000  -- Wait for thread to complete (1 second)
      

MVar for Thread Communication

MVars are mutable variables for thread communication:


import Control.Concurrent

-- MVar example
main :: IO ()
main = do
  mvar <- newEmptyMVar
  forkIO $ do
    putStrLn "Thread is working..."
    threadDelay 500000  -- Simulate work
    putMVar mvar "Result"
  putStrLn "Main thread waiting..."
  result <- takeMVar mvar
  putStrLn ("Got: " ++ result)
      

Software Transactional Memory (STM)

STM provides composable transactions for shared state:


import Control.Concurrent
import Control.Concurrent.STM

-- Bank account example
type Account = TVar Int

transfer :: Account -> Account -> Int -> STM ()
transfer from to amount = do
  fromBalance <- readTVar from
  when (fromBalance < amount) $ retry
  writeTVar from (fromBalance - amount)
  toBalance <- readTVar to
  writeTVar to (toBalance + amount)

main :: IO ()
main = do
  alice <- atomically $ newTVar 100
  bob <- atomically $ newTVar 0
  forkIO $ atomically $ transfer alice bob 50
  threadDelay 1000000
  aliceBal <- atomically $ readTVar alice
  bobBal <- atomically $ readTVar bob
  putStrLn $ "Alice: " ++ show aliceBal ++ ", Bob: " ++ show bobBal
      

Async for Managing Threads

The async package provides higher-level operations:


import Control.Concurrent.Async

-- Running computations in parallel
main :: IO ()
main = do
  (result1, result2) <- concurrently 
    (slowComputation "First") 
    (slowComputation "Second")
  putStrLn (result1 ++ " and " ++ result2)

slowComputation :: String -> IO String
slowComputation name = do
  threadDelay 1000000
  return ("Done: " ++ name)
      

Parallel Evaluation

The parallel package helps with parallel computation:


import Control.Parallel
import Control.Parallel.Strategies

-- Parallel map
parMap :: (a -> b) -> [a] -> [b]
parMap f xs = map f xs `using` parList rdeepseq

-- Example usage
main :: IO ()
main = do
  let results = parMap (\x -> x * x) [1..100]
  print (sum results)
      

Channels for Message Passing

Channels provide FIFO communication between threads:


import Control.Concurrent
import Control.Concurrent.Chan

-- Channel example
main :: IO ()
main = do
  chan <- newChan
  forkIO $ do
    writeChan chan "Hello"
    writeChan chan "World"
  forkIO $ do
    msg1 <- readChan chan
    msg2 <- readChan chan
    putStrLn (msg1 ++ " " ++ msg2)
  threadDelay 1000000
      

Thread States

Haskell threads can be in different states:

Thread Scheduling

The Haskell runtime uses a sophisticated scheduler:

Exceptions in Concurrent Code

Handling exceptions in concurrent code requires care:


import Control.Exception

-- Catching exceptions in threads
main :: IO ()
main = do
  handle (\e -> putStrLn ("Caught: " ++ show (e :: SomeException)) $ do
    thread <- async $ do
      threadDelay 500000
      error "Oops!"
    wait thread
      

Best Practices

Tips for concurrent Haskell programming:

← Previous: Lazy Evaluation | Next: Common Libraries →