2013-04-07 25 views
6

IORef s MVar s TVar sを使用して、共有変数を並行コンテキストでラップすることができます。私は並行してhaskellをしばらく勉強しましたが、今私はいくつかの質問に遭遇しました。 stackoverflowで検索し、いくつかの関連する質問を読んだ後、私の質問は完全に解決されていません。 IORefdocumentationによるとHaskellでスレッドセーフな共有変数を使用する方法

  1. 、「複数のIORefsに原子性を拡張する問題がある」、誰かが単一IORefが安全であるが、複数のIORef sが問題である理由を説明するのを助けることができることは?
  2. modifyMVarは「例外的に安全ですが、このMVarの他のプロデューサがない場合はアトミック」です。 のdocumentationを参照してください。ソースコードは、modifyMVargetMVarputMVarを順番に構成するだけで、別のプロデューサがあればスレッドセーフであることを示しています。しかし、プロデューサがなく、すべてのスレッドが "takeMVar then putMVar"のように振る舞う場合は、スレッドセーフで、単にmodifyMVarを使用しますか?

実際の問題を具体的に説明します。私は空ではない共有変数をいくつか持っていて、それらを可変状態にして、いくつかのスレッドがこれらの変数を同時に変更できるようにしたい。

OK、それはTVarすべてをはっきりと解決するようです。しかし、私はそれに満足していないと私は上記の質問への答えに熱望しています。どんな助けもありがとう。

-------------- RE:以下@GabrielGonzalez BFSインターフェースコード------------------

コードされます私のBFSインターフェースは状態モナドを使用しています。

{-# LANGUAGE TypeFamilies, FlexibleContexts #-} 

module Data.Graph.Par.Class where 

import Data.Ix 
import Data.Monoid 
import Control.Concurrent 
import Control.Concurrent.MVar 
import Control.Monad 
import Control.Monad.Trans.State 

class (Ix (Vertex g), Ord (Edge g), Ord (Path g)) => ParGraph g where 
    type Vertex g :: * 
    type Edge g :: * 
    -- type Path g :: *   -- useless 
    type VertexProperty g :: * 
    type EdgeProperty g :: * 
    edges :: g a -> IO [Edge g] 
    vertexes :: g a -> IO [Vertex g] 
    adjacencies :: g a -> Vertex g -> IO [Vertex g] 
    vertexProperty :: Vertex g -> g a -> IO (VertexProperty g) 
    edgeProperty :: Edge g -> g a -> IO (EdgeProperty g) 
    atomicModifyVertexProperty :: (VertexProperty g -> IO (VertexProperty g)) -> 
           Vertex g -> g a -> IO (g a) -- fixed 

spanForest :: ParGraph g => [Vertex g] -> StateT (g a) IO() 
spanForest roots = parallelise (map spanTree roots) -- parallel version 

spanForestSeq :: ParGraph g => [Vertex g] -> StateT (g a) IO() 
spanForestSeq roots = forM_ roots spanTree -- sequencial version 

spanTree :: ParGraph g => Vertex g -> StateT (g a) IO() 
spanTree root = spanTreeOneStep root >>= \res -> case res of 
    [] -> return() 
    adjs -> spanForestSeq adjs 

spanTreeOneStep :: ParGraph g => Vertex g -> StateT (g a) IO [Vertex g] 
spanTreeOneStep v = StateT $ \g -> adjacencies g v >>= \adjs -> return (adjs, g) 

parallelise :: (ParGraph g, Monoid b) => [StateT (g a) IO b] -> StateT (g a) IO b 
parallelise [] = return mempty 
parallelise ss = syncGraphOp $ map forkGraphOp ss 

forkGraphOp :: (ParGraph g, Monoid b) => StateT (g a) IO b -> StateT (g a) IO (MVar b) 
forkGraphOp t = do 
    s <- get 
    mv <- mapStateT (forkHelper s) t 
    return mv 
    where 
    forkHelper s x = do 
     mv <- newEmptyMVar 
     forkIO $ x >>= \(b, s) -> putMVar mv b 
     return (mv, s) 

syncGraphOp :: (ParGraph g, Monoid b) => [StateT (g a) IO (MVar b)] -> StateT (g a) IO b 
syncGraphOp [] = return mempty 
syncGraphOp ss = collectMVars ss >>= waitResults 
    where 
    collectMVars [] = return [] 
    collectMVars (x:xs) = do 
     mvx <- x 
     mvxs <- collectMVars xs 
     return (mvx:mvxs) 
    waitResults mvs = StateT $ \g -> forM mvs takeMVar >>= \res -> return ((mconcat res), g) 
+0

「TVar」に満足していないのはなぜですか? 'stm'は、Haskellの並行処理に対する非常に洗練されたソリューションです。私はソフトウェアトランザクションメモリを使用して解決できなかった問題には一度も触れていません。 –

+0

'modifyMVar'の動作について言えば、「スレッドセーフティ」が適切な言葉であるかどうかわかりません。あなたのプログラムはsegfaultやpythonのように爆破するつもりはないので、あなたのスレッドにスローされた例外は "無期限にブロックされ"てしまいます。 – jberryman

+0

@ GabrielGonzalez STMのimplementaionは "log then commit"を使用しているため、比較的不安定で、オーバーヘッドが大きくなる可能性があります。しかし、私は効率性を定量的に比較していません。 – pysuxing

答えて

4
  1. 現代のプロセッサは、アトミック単一のポインタを変更コンペアアンドスワップ命令を提供します。十分に深く追跡すると、この指示はatomicModifyIORefを実装するために使用された命令であることがわかります。したがって、単一のポインタにアトミックアクセスを提供することは容易です。しかし、複数のポインタに対してハードウェアサポートがないため、必要なものはソフトウェアで行う必要があります。これには通常、すべてのスレッドでプロトコルを作成し、手動で実施する必要があります。これは複雑でエラーが発生しやすい方法です。

  2. はい、すべてのスレッドが「takeMVarに続いて単一のputMVar」という動作のみを使用することに同意する場合、modifyMVarは安全です。

+0

お返事ありがとうございます!私は実装技術をまだ追跡していません。アトミック性がハードウェアによって制限されている場合、複数のIORefを使用してスレッド間で共有するとどうなりますか? – pysuxing

+1

@「複数のIORefへのアトミック性の拡張は問題がある」という警告は、例えば以下のようなことをすることの難しさを指しています。合計をカウンタ 'a :: IORef Int'から' b :: IORef Int'に原子的に移動し、 'a'をゼロに設定します。他のスレッドが中間状態のいずれも見ないようにする方法はありません(または簡単な方法はありません)。 – jberryman

+0

@jberrymanこの例では、2つ以上の 'IORef'演算を1つのアトミック演算にまとめることは不可能です。しかし、マルチスレッディング・コンテキストでマルチプルなIORefを使用しても、問題は発生しません。 – pysuxing

関連する問題