Can I safely assign to non-overlapping indices of an IOVector (from the Haskell vector package) from multiple parallel threads, or do I need an additional mutex?
Background: I want to run a collection of IO computations in parallel and then record all the results. I started by returning the results in a list, but this seems like the wrong data structure. So I am looking to use an IOVector from the vector package to store the results.
My question is, can I write something like:
runPar :: [IO a] -> IO (IOVector a)
runPar tasks = do
v <- new (length tasks)
mapM forkIO [task >>= write v i | (i, task) <- zip [0..] tasks]
-- Wait for all tasks to complete
return v
Is this guaranteed to be safe? Or do I need to have a mutex-like control (e.g. holding v in an MVar
) to make sure that only one write proceeds at a time?
From the code and the discussions in the comments, there seems to be no reason to assume it is not thread-safe. And just to support this, here is a little test for it:
import qualified Data.Vector.Mutable as V
import System.Environment
import Control.Concurrent.Async
thread v i 0 = return ()
thread v i n = do
x <- V.read v i
V.write v i $! x-1
thread v i (n-1)
main = do
[m,n] <- map read `fmap` getArgs
v <- V.replicate m n
tis <- mapM (\i -> async (thread v i n)) [0..m-1]
mapM_ wait tis
r <- mapM (V.read v) [0..m-1]
if all (== 0) r then putStrLn "OK" else putStrLn $ "Not OK: " ++ show r
Here, even with 100 threads spawned in parallel, counting down from 1000000, with -RTS -N
or -RTS -N10
, always yields OK
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With