Is the same "global random number generator" shared across all threads, or does each thread get its own?
If one is shared, how can I ensure thread-safety? The approach using getStdGen and setStdGen described in the "Monads" chapter of Real World Haskell doesn't look safe.
If each thread has an independent generator, will the generators for two threads started in rapid succession have different seeds? (They won't, for example, if the seed is a time in seconds, but milliseconds might be OK. I don't see how to get a time with millisecond resolution from Data.Time.)
Random are thread-safe. However, the concurrent use of the same java. util. Random instance across threads is synchronized and as we have found out tends to trigger contention issues affecting performance of the application.
The function rand() is not reentrant or thread-safe, since it uses hidden state that is modified on each call.
random() guarantees it's safe for use by multiple threads. But the Random class does not.
This is due to an implementation detail of Random that does not tolerate multithreaded access (it wasn't designed for it, and the docs explicitly call out that Random should not be used from multiple threads, as they do for most types in the . NET Framework). // over with the new number.
There is a function named newStdGen
, which gives one a new std. gen every time it's called. Its implementation uses atomicModifyIORef
and thus is thread-safe.
newStdGen
is better than get/setStdGen not only in terms of thread-safety, but it also guards you from potential single-threaded bugs like this: let rnd = (fst . randomR (1,5)) <$> getStdGen in (==) <$> rnd <*> rnd
.
In addition, if you think about the semantics of newStdGen
vs getStdGen
/setStdGen
, the first ones can be very simple: you just get a new std. gen in a random state, chosen non-deterministically. On the other hand, with the get/set pair you can't abstract away the global program state, which is bad for multiple reasons.
I would suggest you to use getStdGen
only once (in the main thread) and then use the split
function to generate new generators. I would do it like this:
Make an MVar
that contains the generator. Whenever a thread needs a new generator, it takes the current value out of the MVar
, calls split
and puts the new generator back. Due to the functionality of an MVar
, this should be threadsafe.
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