Talk:Parallelism vs. Concurrency
Jump to navigation
Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
Parallelism vs concurrency: what's the difference?
Visible side effects:
- Have a look at this
ugly eysore"prototype definition" ofpar
:
par :: a -> b -> b par x y = case unsafeLocalState (forkIO (evaluate x >> return ())) of !_ -> y
where:
evaluate :: a -> IO a forkIO :: IO () -> IO ThreadId
Assuming:
x
is well-defined (it contains nounsafe...
calls),x
is well-behaved (not throwing exceptions or causing errors);
then:
forkIO
attaches aThreadId
to its argument, adds it to the work-queue and returns the identifier;par
then returnsy
;- Some time later,
forkIO
's argument is called, causingevaluate
to start evaluatingx
.
If
y
is still being evaluated when the evaluation ofx
commences, then we have concurrency, but with no visible side-effects - it behaves like elementary parallelism.
- Now have a look at this
nearly-as-uglyprototype definition forspawnIO
forkIO
:
forkIO :: IO () -> IO ThreadId forkIO act = do v <- newEmptyMVar let thr = do i <- myThreadId putMVar v i act z <- unsafeInterleaveIO thr par z (takeMVar v)
where:
myThreadId :: IO ThreadId newEmptyMVar :: IO (MVar a) putMVar :: MVar a -> a -> IO () takeMVar :: MVar a -> IO a par :: a -> b -> b
Assuming
par
,newEmptyMVar
,putMVar
andtakeMVar
are primitive,then:
- An unused
MVar
is obtained:v
; - the parameter
act
is used to buildthr
which will store itsThreadId
inv
; z
, the future result ofthr
, is then retrieved lazily by usingunsafeInterleaveIO
;par
then presentsz
for parallel evaluation;- putMVar waits while
v
is empty; - putMVar then returns the contents of
v
.
This is parallelism, but having visible side effects - it behaves like elementary concurrency.
- An unused
Can either prototype definition potentially go mainstream?
- As shown by it's type signature,
par
is meant to have no visible effects: avoiding the use ofunsafeLocalState
means making it primitive; - While the use of
unsafeInterleaveIO
may annoy some, it being one of the earlier Haskell extensions means it's widely available.
For now, using a primitive par
(and others) to define forkIO
looks like the simplest option...but if using unsafeInterleaveIO
really does annoy you, how about this:
forkIO :: (OI -> ()) -> OI -> ThreadId forkIO act u = let !(u1:u2:u3:u4:u5:_) = parts u in let !v = newEmptyMVar u1 let z = let !i = myThreadId u2 in let !_ = putMVar v i u3 in let !_ = act u4 in () in par z (takeMVar v u5)
-- Atravers Tue Apr 20 06:04:10 UTC 2021