s MVar
s TVar
s는 동시 컨텍스트에서 공유 변수를 래핑하는 데 사용할 수 있습니다. 나는 잠시 동안 동시성 해법을 배웠고, 이제 나는 몇 가지 질문을 겪었다. stackoverflow 검색 및 관련 질문을 통해 읽은 후 내 질문에 완전히 해결되지 않습니다.Haskell에서 스레드 안전 공유 변수를 사용하는 방법
- 사람이 하나의
IORef
안전하지만 더 이상의IORef
의 문제가있는 이유를 설명하는 데 도움이 될 수 있습니다, "여러 IORefs에 대한 자성이 문제가 확장"는IORef
documentation에 따르면? modifyMVar
은 "예외가 발생하지 않지만이 MVar의 다른 제작자가없는 경우에만 아토믹"입니다.MVar
의 documentation을 참조하십시오. 소스 코드는modifyMVar
이getMVar
과putMVar
만을 순차적으로 구성한다는 것을 보여 주며, 다른 제작자가있는 경우 스레드로부터 안전하다는 것을 나타냅니다. 그러나 생산자가없고 모든 스레드가 "takeMVar
thenputMVar
"이라는 방식으로 동작하는 경우 스레드를 사용하면 스레드가 안전합니까?modifyMVar
?
구체적인 상황을 설명하기 위해 실제 문제를 보여 드리겠습니다. 나는 결코 비어 있지 않은 공유 변수를 가지고 있는데, 변수를 변경 가능 상태로 유지하여 일부 스레드가 동시에 이러한 변수를 수정할 수있게하고 싶습니다.
좋아, 그게 전부 TVar
분명히 해결 될 것 같습니다. 그러나 나는 그것에 만족하지 않고 위에 열거 한 질문에 대한 답을 원합니다. 어떤 도움을 주셔서 감사합니다.
는 -------------- 재 : @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의 동시성에 대한 정말 우아한 솔루션입니다. 소프트웨어 트랜잭션 메모리를 사용하여 해결할 수없는 문제를 본 적이 없습니다. –
"thread-safety"가'modifyMVar'의 동작에 대해 이야기 할 때 적절한 단어인지 확신 할 수 없습니다. 당신의 프로그램은 segfault 나 python처럼 날려 버리지 않을 것입니다. 스레드에서 던져진 예외가 "무기한으로 차단"될 것입니다. – jberryman
@ GabrielGonzalez "log then commit"을 사용하는 STM의 구현은 상대적으로 불안정하고 너무 많은 오버 헤드를 유발할 수 있습니다. 그러나 효율성을 정량적으로 비교하지는 못했습니다. – pysuxing