Difference between revisions of "Talk:Parallelism vs. Concurrency"
Jump to navigation
Jump to search
(Using unsafeInterleaveIO where applicable) |
m (GHC provides "evaluate", not "evalIO"...) |
||
Line 8: | Line 8: | ||
par :: a -> b -> b |
par :: a -> b -> b |
||
par x y = case |
par x y = case |
||
− | unsafeLocalState (forkIO ( |
+ | unsafeLocalState (forkIO (evaluate x >> return ())) |
of |
of |
||
!_ -> y |
!_ -> y |
||
Line 16: | Line 16: | ||
<haskell> |
<haskell> |
||
− | + | evaluate :: a -> IO a |
|
− | forkIO :: IO () -> IO ThreadId |
+ | forkIO :: IO () -> IO ThreadId |
</haskell> |
</haskell> |
||
Line 27: | Line 27: | ||
# <code>forkIO</code> attaches a <code>ThreadId</code> to its argument, adds it to the work-queue and returns the identifier; |
# <code>forkIO</code> attaches a <code>ThreadId</code> to its argument, adds it to the work-queue and returns the identifier; |
||
# <code>par</code> then returns <code>y</code>; |
# <code>par</code> then returns <code>y</code>; |
||
− | # Some time later, <code>forkIO</code>'s argument is called, causing <code> |
+ | # Some time later, <code>forkIO</code>'s argument is called, causing <code>evaluate</code> to start evaluating <code>x</code>. |
If <code>y</code> is still being evaluated when the evaluation of <code>x</code> commences, then we have elementary parallelism: concurrency, but with no visible side-effects. |
If <code>y</code> is still being evaluated when the evaluation of <code>x</code> commences, then we have elementary parallelism: concurrency, but with no visible side-effects. |
Revision as of 05:25, 21 April 2021
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 elementary parallelism: concurrency, but with no visible side-effects.
- Now have a look at this
nearly-as-uglyprototype definition forspawnIO
forkIO
:
forkIO :: IO () -> IO ThreadId forkIO act = do t <- unsafeInterleaveIO act case par t () of !_ -> do i' <- itsThreadId t case i' of Just i -> return i Nothing -> ioError "forkIO"
where:
itsThreadId :: a -> IO (Maybe ThreadId)
Assuming:
par
anditsThreadId
are primitive,itsThreadId
would returnNothing
if it's argument had not been previously used bypar
;
then:
- Evaluating
par t ()
causes a newThreadId
to be attached tot
by the implementation; itsThreadId
retrievesi'
, the (possible) identifier fort
;forkIO
then extracts and returns the identifier.
This looks very much like elementary concurrency: parallelism, but having visible side effects.
Can either of these prototypes ever go mainstream?
- As shown by it's type signature,
par
is supposed to be pure: 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:_) = parts u in let t = act u1 in case par t () of !_ -> case itsThreadId t u2 of Just i -> i Nothing -> ioError "forkIO" u3
-- Atravers Tue Apr 20 06:04:10 UTC 2021