Why is putStrLn not atomic?
To practice parallel programming, I wrote the following (suboptimal) program that repeatedly calculates the first prime more than anything the user enters:
import Control.Concurrent
import Control.Concurrent.Chan
import Control.Monad (forever)
primeAtLeast n = -- Some pure code that looks up the first prime at least as big as n
outputPrimeAtLeast n = putStrLn $ show $ (n, primeAtLeast n)
main = do
chan <- newChan
worker <- forkIO $ forever $ readChan chan >>= outputPrimeAtLeast
forever $ (readLn :: (IO Int)) >>= (writeChan chan)
killThread worker
I want to have a worker thread in the background that does the actual calculation and outputs (n, primeAtLeast n)
as soon as it ends.
What it does now: As soon as I enter a number n
, it outputs immediately (n,
, returns the control to the main thread, computes primeAtLeast n
in the background, and outputs the other half primeAtLeast n)
as soon as it finishes.
So, putStrLn
not atomic? Or where is the problem?
source to share
Try the following:
outputPrimeAtLeast n = let p = primeAtLeast n in p `seq` putStrLn $ show (n, p)
The above forces it to compute the space before starting putStrLn
.
Alternatively, you can use print
instead putStrLn . show
:
outputPrimeAtLeast n = let p = primeAtLeast n in p `seq` print (n, p)
Alternatively, you can use a function putStrLn
that forces each individual character before printing. In the meantime, there is no need to worry about it. ”
strictPutStrLn :: Show a => a -> IO ()
strictPutStrLn x = let str = show x in str `listSeq` putStrLn str
listSeq :: [a] -> b -> b
listSeq [] w = w
listSeq (x:xs) w = x `seq` listSeq xs w
source to share