サーチ…


備考

Haskellにおける並行プログラミングと並列プログラミングについて学ぶための優れたリソースは次のとおりです。

`forkIO`を使ってスレッドを生成する

Haskellは多くの形式の並行処理をサポートしており、 forkIOを使用してスレッドをフォークすることが最も明白forkIO

関数forkIO :: IO () -> IO ThreadIdIOアクションをThreadIdThreadIdを返します。一方、アクションはバックグラウンドで実行されます。

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. スレッド1が読みmaとブロックma
  2. スレッド2は読み込みmbので、ブロックmb

スレッド1はスレッド2がブロックしているので、スレッド1はmbを読み取ることができず、スレッド1はスレッド1がブロックしているのでスレッド2はmaを読み取ることができません。古典的なデッドロック!

ソフトウェアトランザクションメモリを持つアトミックブロック

Haskellのもう一つの強力で成熟した並行性ツールは、Software Transactional Memoryです。これは、複数のスレッドが、 TVar a型の単一の変数に原子的に書き込むことを可能にします。

TVar aSTMモナドに関連する主なタイプであり、トランザクション変数の略です。これらは、 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


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow