IORef
s MVar
s TVar
sを使用して、共有変数を並行コンテキストでラップすることができます。私は並行してhaskellをしばらく勉強しましたが、今私はいくつかの質問に遭遇しました。 stackoverflowで検索し、いくつかの関連する質問を読んだ後、私の質問は完全に解決されていません。 IORef
documentationによるとHaskellでスレッドセーフな共有変数を使用する方法
- 、「複数のIORefsに原子性を拡張する問題がある」、誰かが単一
IORef
が安全であるが、複数のIORef
sが問題である理由を説明するのを助けることができることは? modifyMVar
は「例外的に安全ですが、このMVarの他のプロデューサがない場合はアトミック」です。 のdocumentationを参照してください。ソースコードは、modifyMVar
がgetMVar
とputMVar
を順番に構成するだけで、別のプロデューサがあればスレッドセーフであることを示しています。しかし、プロデューサがなく、すべてのスレッドが "takeMVar
thenputMVar
"のように振る舞う場合は、スレッドセーフで、単に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)
「TVar」に満足していないのはなぜですか? 'stm'は、Haskellの並行処理に対する非常に洗練されたソリューションです。私はソフトウェアトランザクションメモリを使用して解決できなかった問題には一度も触れていません。 –
'modifyMVar'の動作について言えば、「スレッドセーフティ」が適切な言葉であるかどうかわかりません。あなたのプログラムはsegfaultやpythonのように爆破するつもりはないので、あなたのスレッドにスローされた例外は "無期限にブロックされ"てしまいます。 – jberryman
@ GabrielGonzalez STMのimplementaionは "log then commit"を使用しているため、比較的不安定で、オーバーヘッドが大きくなる可能性があります。しかし、私は効率性を定量的に比較していません。 – pysuxing