Channel Synchronization in Haskell

In Haskell, we can use MVars for synchronization across threads. Here’s an example of using a blocking receive to wait for a thread to finish. When waiting for multiple threads to finish, you may prefer to use a similar mechanism to WaitGroup, which can be implemented using MVars.

import Control.Concurrent (forkIO, threadDelay, MVar, newEmptyMVar, putMVar, takeMVar)
import System.IO (hFlush, stdout)

-- This is the function we'll run in a separate thread. The
-- `done` MVar will be used to notify the main thread that 
-- this function's work is done.
worker :: MVar Bool -> IO ()
worker done = do
    putStr "working..."
    hFlush stdout
    threadDelay 1000000  -- Sleep for 1 second
    putStrLn "done"
    -- Send a value to notify that we're done.
    putMVar done True

main :: IO ()
main = do
    -- Create an empty MVar to use for synchronization
    done <- newEmptyMVar
    
    -- Start a worker thread, giving it the MVar to notify on.
    forkIO $ worker done
    
    -- Block until we receive a notification from the
    -- worker on the MVar.
    takeMVar done

To run the program:

$ runhaskell channel-synchronization.hs
working...done

If you removed the takeMVar done line from this program, the program would exit before the worker even started.

In this Haskell version:

  1. We use MVar instead of channels for synchronization.
  2. The forkIO function is used to start a new thread, similar to goroutines in Go.
  3. threadDelay is used to simulate work, similar to time.Sleep in Go.
  4. putMVar is used to send a signal, similar to sending on a channel in Go.
  5. takeMVar is used to wait for the signal, similar to receiving from a channel in Go.

This example demonstrates how to use MVars in Haskell for basic thread synchronization, which is conceptually similar to using channels for goroutine synchronization in Go.