Concurrency: Threads and MVar

Compile with -threaded and run with +RTS -N to use multiple OS threads/cores.

MVar is a box that's either empty or full: takeMVar blocks when empty, putMVar blocks when full. It's a lock, a one-shot channel, and a synchronization primitive in one. It's prone to deadlocks when combined; prefer STM for composite state.

import Control.Concurrent
import Control.Monad

main :: IO ()
main = do
  -- forkIO creates a lightweight (green) thread: you can create millions
  done <- newEmptyMVar
  _ <- forkIO $ do
    forM_ [1 .. 3 :: Int] $ \i -> putStrLn ("worker: " ++ show i) >> threadDelay 100000   -- microseconds
    putMVar done ()
  putStrLn "main continues"
  takeMVar done                        -- block until the worker signals

  -- MVar as a lock-protected shared variable
  counter <- newMVar (0 :: Int)
  finished <- newEmptyMVar
  forM_ [1 .. 10 :: Int] $ \_ -> forkIO $ do
    replicateM_ 1000 (modifyMVar_ counter (pure . (+ 1)))
    putMVar finished ()
  replicateM_ 10 (takeMVar finished)
  readMVar counter >>= print           -- 10000

  -- Channels
  chan <- newChan
  _ <- forkIO $ mapM_ (writeChan chan) [1 .. 5 :: Int]
  replicateM 5 (readChan chan) >>= print