Threads

Threads are subroutines that can run concurrently. Every Haskell program begins with one thread, called the main thread. Starting an additional thread is called forking a thread.

To fork a thread, use the forkIO function from the Control.Concurrent module.

import Control.Concurrent (forkIO)
import Control.Concurrent.STM.TVar
import Control.Monad.STM
import Data.Foldable (for_)
import System.IO
main =
  do

Setting the output stream’s buffer mode to line-buffering will make the output of this example more readable.

    hSetBuffering stdout LineBuffering

    tasksCompleted <- atomically (newTVar 0)
    let

We define a function called task, which:

        task x =
          do
  1. Prints three times; then
            for_ [1..3] $ \i ->
                putStrLn (x ++ ": " ++ show i)
  1. Increments the taskCompleted variable.
            atomically $
                modifyTVar' tasksCompleted (+ 1)

We run task three times: Once in the main thread, and then twice in new forked threads.

    task "main"
    forkIO (task "forkA")
    forkIO (task "forkB")

At this point, our two forked threads are now running, and we do not want the main thread to end (thus terminating the program) before the other threads have time to finish. So we wait until the value of tasksCompleted reaches 3.

    atomically $
      do
        x <- readTVar tasksCompleted
        check (x == 3)

    putStrLn "done"
$ runhaskell threads.hs

The lines from task "main" are printed first; no fork has occurred yet.

main: 1
main: 2
main: 3

Because task "forkA" and task "forkB" run concurrently, their putStrLn effects are interleaved.

forkA: 1
forkB: 1
forkA: 2
forkB: 2
forkA: 3
forkB: 3
done

The interleaving is nondeterministic; if you run this program multiple times, you may see different orderings.

$ runhaskell threads.hs
main: 1
main: 2
main: 3
forkA: 1
forkB: 1
forkB: 2
forkB: 3
forkA: 2
forkA: 3
done

Next: