Random
is not thread-safe - you must not use the same Random
instance from multiple threads without synchronization.
Why are you getting 1 in particular? Well, the way Random
works (in 4.5.2) is by keeping a seed array as well as two indexers. When you use it from multiple threads concurrently, your seed array is going to get all messed up, and you'll almost always get identical values in multiple slots. The basic operation does something like seed[a] - seed[b]
, and when those values are the same, you get zero back. Since you asked for 1 as a minimum, this zero is shifted to one - and there's your anomaly. This happens very quickly in a multi-core environment, since there's quite a lot of inter-dependent state that's updated on each Next
call.
There's many ways to solve this problem. One is to synchronize access to a common Random
instance - it only makes sense if you're doing relatively few randoms, though, in which case you wouldn't use Parallel
anyway. If performance is a concern, you'll either need to add some form of pre-fetching (e.g. preparing the random numbers in batches, per-thread or using some concurrent queue), or use some other method.
Another way is to keep a separate Random
instance for each thread. This requires you to carefuly select a seed for each of the instances, though, otherwise your random numbers may end up being quite non-random. The approach used in .NET itself (again, using 4.5.2 code for reference) is to use Thread.CurrentThread.ManagedThreadId
as the seed, which works quite well. Another common approach is to use a single global (synchronized) Random
instance to initialize the seeds of the other Random
s, but depending on your requirements, you may need to ensure no duplicate seeds are produced.
Of course, you could also use some other random number generator. However, pseudo-random generators will usually require the same approaches as Random
- they heavily depend on their state; that's what makes them pseudo-random in the first place. A cryptographic generator may work better, but those tend to be very slow, and may fallback to the synchronized approach anyway, especially if there's no hardware support.
In some cases, it makes sense to distribute the generation work according to some reasonable rules. For example, if you use pseudo-random procedural generation for in-game assets, it may make sense to make explicit rules to how different generators are seeded, in a repeatable manner - of course, this also means you can't really use Parallel
either, and you'll have to be a bit more explicit.