concurrency - How to use thread safe shared variables in Haskell -
iorefs, mvars, , tvars can used wrap shared variable in concurrent context. i've studied concurrent haskell while , i've encounted questions. after searching on stackoverflow , read through related question, questions not resolved.
- according
iorefdocumentation,"extending atomicity multiple iorefs problematic", can explain why singleiorefsafe more 1iorefs problematic? modifymvar"exception-safe, atomic if there no other producers mvar". seemvar's documentation. source code showmodifymvarcomposegetmvar,putmvarsequencially, indicating it's note thread-safe if there producer. if there no producer , threads behave in "takemvarputmvar" way, thread-safe usemodifymvar?
to give concrete situation, i'll show actual problem. i've shared variables never empty , want them mutable states threads can simultaneously modify these variables.
ok, seems tha tvar solve clearly. i'm not satisfied , i'm eager answers questions above. appreciated.
-------------- re: @gabrielgonzalez bfs interface code ------------------
code below bfs interface using state monad.
{-# language typefamilies, flexiblecontexts #-} module data.graph.par.class 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 type vertex g :: * type edge g :: * -- type path g :: * -- useless type vertexproperty g :: * type edgeproperty g :: * edges :: g -> io [edge g] vertexes :: g -> io [vertex g] adjacencies :: g -> vertex g -> io [vertex g] vertexproperty :: vertex g -> g -> io (vertexproperty g) edgeproperty :: edge g -> g -> io (edgeproperty g) atomicmodifyvertexproperty :: (vertexproperty g -> io (vertexproperty g)) -> vertex g -> g -> 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 = s <- mv <- mapstatet (forkhelper s) t return mv forkhelper s x = 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 collectmvars [] = return [] collectmvars (x:xs) = mvx <- x mvxs <- collectmvars xs return (mvx:mvxs) waitresults mvs = statet $ \g -> form mvs takemvar >>= \res -> return ((mconcat res), g)
modern processors offer compare-and-swap instruction atomically modifies single pointer. expect if track down deep enough, find instruction 1 used implement
atomicmodifyioref. therefore easy provide atomic access single pointer. however, because there isn't such hardware support more 1 pointer, whatever need have done in software. typically involves inventing , manually enforcing protocol in threads -- complicated , error-prone.yes, if threads agree use "single
takemvarfollowed singleputmvar" behavior,modifymvarsafe.
Comments
Post a Comment