Control.Concurrent
を使うとHaskellでもマルチスレッド処理ができます。
よく使う函数は(多分)以下の通り。
forkIO :: IO () -> IO ThreadId -- IO ()を渡すとその場で実行
killThread :: ThreadId -> IO () -- 指定のThreadをkillする
threadDelay :: Int -> IO () -- スレッドを指定の時間[microsec]だけ停止する
data MVar a -- スレッド内で使えるmutableな変数
newMVar :: a -> IO (MVar a) -- 変数を初期化
takeMVar :: MVar a -> IO a -- 変数の値を取り出す
putMVar :: MVar a -> a -> IO () -- 変数に代入する
サンプルコードその1
import Control.Concurrent
import Control.Monad
waiting :: Int -> IO ()
waiting n = do
putStrLn $ "waiting" ++ (replicate n '.')
threadDelay 100000
waiting (n+1)
doSth :: IO Bool
doSth = do
-- do something
threadDelay 3000000
putStrLn $ "=========== done ========="
threadDelay 3000000
putStrLn $ "=========== done ========="
threadDelay 3000000
putStrLn $ "=========== done ========="
return True
ex1 = do
w <- forkIO $ waiting 0
u <- doSth
when u $ killThread w
putStrLn "killed the waiting thread"
doSth
スレッドは「3秒かかる処理」を3回行います。
waiting
スレッドは"waiting..."というメッセージを出力します。処理が3回終わったところで待ちスレッドをkillしておしまいです。
サンプルコードその2
import Control.Concurrent
import Control.Monad
waiting :: Int -> IO ()
waiting n = do
putStrLn $ "waiting" ++ (replicate n '.')
threadDelay 100000
waiting (n+1)
doSthWithMVar :: MVar Bool -> IO ()
doSthWithMVar ref = do
threadDelay 3000000
putStrLn $ "=========== done ========="
threadDelay 3000000
putStrLn $ "=========== done ========="
threadDelay 3000000
putStrLn $ "=========== done ========="
putMVar ref True
ex2 = do
ref <- newMVar False
w <- forkIO $ waiting 0
_ <- forkIO $ doSthWithMVar ref
go ref w
where
go :: MVar Bool -> ThreadId -> IO ()
go ref w = do
tf <- takeMVar ref
case tf of
True -> do
killThread w
putStrLn "killed the waiting thread"
False -> go ref w
大体さっきと同じですが、今回はMVar
を使ってフラグを管理しています。
doSthWithMVar
は、3秒かかる処理を3回したあとフラグをTrue
にします。
ex2
は2つのスレッドを立ち上げたあとループに入ります(go
)。フラグがTrue
になったらスレッドをkillしてループを抜けます。
これだけ!
まとめ
-
Control.Concurrent
のThreadはとても簡単で使いやすい -
MVar
を使えばスレッド間で共有したいmutableな変数を作成することができる - こころなしか(特に日本語)ググってもサンプル・チュートリアルが少ないような…[要出典]