Talk:Parallelism vs. Concurrency
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