Control.Concurrent.STM
STMってのはSoftware Transactional Memoryの頭文字。
- [id:bellbind:20051215:1134609968]
基本はTVar aに対し、読み書きする機能。ほぼData.IORefと同じようにnew/read/writeして使う。
- 対象: IORef → TVar
- 処理: IO → STM
ただし、機能はSTM(モナド)型であるため、Threadで使うIOにするために、atomicallyでくくることになる。その部分は排他的に処理が行われる。
以下は単純に整数を読み書きする例
import Control.Concurrent import Control.Concurrent.STM main = do var <- atomically (newTVar 0) forkIO (myproc var "p1") forkIO (myproc var "p2") getLine return () myproc :: TVar Int -> String -> IO () myproc var name = do v <- atomically (readWrite var) putStrLn (name ++ ": " ++ v) yield -- 基本はプリエンティブのようなので切り替え myproc var name -- 無限ループ where readWrite :: TVar Int -> STM String readWrite var = do v <- readTVar var let newV = v + 1 writeTVar var newV return $ show newV
排他的にインクリメントしてるので、同じ数は二つ出力されない。
STMの特徴はSTM処理の中でretryすることができる。TVar変数が条件に合わないとき(たとえば、空リストのとき、とか)、retryすると再び処理が走る。
以下、リストを使った消費者生産者処理。
main = do var <- atomically (newTVar []) forkIO $ produce var forkIO $ consume var getLine return () produce var = prod var 0 where prod var n = do atomically (post var n) yield prod var (n + 1) post var n = do l <- readTVar var let newL = l ++ [n] writeTVar var newL return () consume var = do v <- atomically (pop var) putStrLn $ "consume: " ++ (show v) consume var where pop var = do l <- readTVar var case l of [] -> retry -- 空なのでリトライ (h:t) -> do writeTVar var t return h
retryされたとき、別の処理を走らせるにはSTM処理をorElseでつなぐことができる。どんな例があるだろうか。