Haskellで並行処理
forkIOでスレッドを起動できる。forkIOは(IOモナドに包まれた)スレッドIDを返すので、取っておいて後でkillThreadするとThreadKilled例外を投げて終了できる。
import Control.Concurrent main :: IO () main = do id <- forkIO $ subThread 0 threadDelay 5000000 killThread id subThread :: Int -> IO () subThread num = do putStrLn $ "loop " ++ (show num) threadDelay 1000000 subThread $ num + 1
スレッド間でメッセージを送受信したい場合は、MVarを使う。MVarは容量1のメッセージボックスで、既にメッセージが入ってる時に更に書き込もうとするとブロックするし、逆にメッセージが無い時に読み込もうとしてもブロックする。ChanはMVarの容量無制限バージョンで、書き込みはブロックしない。
import Control.Concurrent main :: IO () main = do numMVar <- newEmptyMVar id <- forkIO $ subThread numMVar threadDelay 1000000 putMVar numMVar 0 threadDelay 4000000 currentNum <- takeMVar numMVar --空の場合はブロック putStrLn $ "numMVar: " ++ (show currentNum) killThread id --subThread :: f Int -> IO () subThread numMVar = do currentNum <- takeMVar numMVar --上のputMVar numMVar 0が来るまでは空なのでブロック putStrLn $ "loop " ++ (show currentNum) threadDelay 1000000 putMVar numMVar $ currentNum + 1 subThread $ numMVar
スレッド間で変数を共有したい場合は、IORefを使ってatomicModifyIORefで更新する。atomicModifyIORefには「IORef型の変数」と「IORefが持つ値を受け取って(更新後の値, atomicModifyIORefの戻り値にしたい値)を返す関数」を渡す。
import Control.Concurrent import Data.IORef main :: IO () main = do numRef <- newIORef 0 id <- forkIO $ subThread numRef threadDelay 1000000 _ <- atomicModifyIORef numRef (\x -> (x+10, x)) threadDelay 4000000 currentNum <- readIORef numRef putStrLn $ "numRef: " ++ (show currentNum) killThread id subThread :: IORef Int -> IO () subThread numRef = do currentNum <- atomicModifyIORef numRef (\x -> (x+1, x)) putStrLn $ "loop " ++ (show currentNum) threadDelay 1000000 subThread $ numRef
IORefの場合アトミックにできるのはatomicModifyIORefに渡した関数だけなので、複数のIORefを扱いたい時はSTMを使うといいらしい……けどややこしいので略。