Haskell Language Atomic Blocks with Software Transactional Memory


Example

Another powerful & mature concurrency tool in Haskell is Software Transactional Memory, which allows for multiple threads to write to a single variable of type TVar a in an atomic manner.

TVar a is the main type associated with the STM monad and stands for transactional variable. They're used much like MVar but within the STM monad through the following functions:

atomically :: STM a -> IO a

Perform a series of STM actions atomically.

readTVar :: TVar a -> STM a

Read the TVar's value, e.g.:

value <- readTVar t

writeTVar :: TVar a -> a -> STM ()

Write a value to the given TVar.

t <- newTVar Nothing
writeTVar t (Just "Hello")

This example is taken from the Haskell Wiki:

import Control.Monad
import Control.Concurrent
import Control.Concurrent.STM
 
main = do 
  -- Initialise a new TVar
  shared <- atomically $ newTVar 0
  -- Read the value
  before <- atomRead shared
  putStrLn $ "Before: " ++ show before
  forkIO $ 25 `timesDo` (dispVar shared >> milliSleep 20)
  forkIO $ 10 `timesDo` (appV ((+) 2) shared >> milliSleep 50)
  forkIO $ 20 `timesDo` (appV pred shared >> milliSleep 25)
  milliSleep 800
  after <- atomRead shared
  putStrLn $ "After: " ++ show after
  where timesDo = replicateM_
       milliSleep = threadDelay . (*) 1000

atomRead = atomically . readTVar
dispVar x = atomRead x >>= print
appV fn x = atomically $ readTVar x >>= writeTVar x . fn