Haskell Language
並行性
サーチ…
備考
Haskellにおける並行プログラミングと並列プログラミングについて学ぶための優れたリソースは次のとおりです。
`forkIO`を使ってスレッドを生成する
Haskellは多くの形式の並行処理をサポートしており、 forkIO
を使用してスレッドをフォークすることが最も明白forkIO
。
関数forkIO :: IO () -> IO ThreadId
はIO
アクションをThreadId
、 ThreadId
を返します。一方、アクションはバックグラウンドで実行されます。
ghci
を使用してこれを非常に簡潔に示すことができghci
:
Prelude Control.Concurrent> forkIO $ (print . sum) [1..100000000]
ThreadId 290
Prelude Control.Concurrent> forkIO $ print "hi!"
"hi!"
-- some time later....
Prelude Control.Concurrent> 50000005000000
どちらのアクションもバックグラウンドで実行され、2つ目のアクションは最後までほぼ完了することが保証されています!
`MVar`によるスレッド間の通信
MVar a
を使用してスレッド間で情報を渡すことは非常に簡単で、 Control.Concurrent
型とそれに付随する関数は次のとおりです。
-
newEmptyMVar :: IO (MVar a)
- 新しいMVar a
作成するMVar a
-
newMVar :: a -> IO (MVar a)
- 指定された値で新しいMVar
を作成する -
takeMVar :: MVar a -> IO a
- 指定されたMVar
から値を取得するか、使用可能になるまでブロックする -
putMVar :: MVar a -> a -> IO ()
- 指定された値をMVar
するか、空になるまでブロックする
スレッドで1〜1億の数値を合計し、結果を待ちましょう:
import Control.Concurrent
main = do
m <- newEmptyMVar
forkIO $ putMVar m $ sum [1..10000000]
print =<< takeMVar m -- takeMVar will block 'til m is non-empty!
より複雑なデモンストレーションは、入力を待っている間にバックグラウンドでユーザーの入力と合計を取ることです。
main2 = loop
where
loop = do
m <- newEmptyMVar
n <- getLine
putStrLn "Calculating. Please wait"
-- In another thread, parse the user input and sum
forkIO $ putMVar m $ sum [1..(read n :: Int)]
-- In another thread, wait 'til the sum's complete then print it
forkIO $ print =<< takeMVar m
loop
先に述べたように、 takeMVar
を呼び出すとMVar
が空であれば、別のスレッドが何かをMVar
入れるまでブロックされ、 食事の哲学者の問題が発生する可能性があります。同じことがputMVar
起こりputMVar
:もしそれがいっぱいであれば、それは空になるまでブロックされます!
次の機能を実行します。
concurrent ma mb = do
a <- takeMVar ma
b <- takeMVar mb
putMVar ma a
putMVar mb b
いくつかのMVar
使って2つの関数を実行します
concurrent ma mb -- new thread 1
concurrent mb ma -- new thread 2
起こりうることは、次のとおりです。
- スレッド1が読み
ma
とブロックma
- スレッド2は読み込み
mb
ので、ブロックmb
スレッド1はスレッド2がブロックしているので、スレッド1はmb
を読み取ることができず、スレッド1はスレッド1がブロックしているのでスレッド2はma
を読み取ることができません。古典的なデッドロック!
ソフトウェアトランザクションメモリを持つアトミックブロック
Haskellのもう一つの強力で成熟した並行性ツールは、Software Transactional Memoryです。これは、複数のスレッドが、 TVar a
型の単一の変数に原子的に書き込むことを可能にします。
TVar a
はSTM
モナドに関連する主なタイプであり、トランザクション変数の略です。これらは、 MVar
よく似ていますが、 STM
モナド内では次の機能を使用しています。
atomically :: STM a -> IO a
一連のSTMアクションをアトミックに実行します。
readTVar :: TVar a -> STM a
TVar
の値を読んでください。例:
value <- readTVar t
writeTVar :: TVar a -> a -> STM ()
指定されたTVar
値を書き込みます。
t <- newTVar Nothing writeTVar t (Just "Hello")
この例は、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