Concurrency issues with Random in .Net?
I've debugging some problem with a Paint.Net plugin and I've stumbled with some issue with the Random class, when several threads call a method from a single instance.
For some strange reason, it seems that if I do not prevent concurrent access, by synchronizing the called method, my Random instance starts to behave... randomly (but in the bad sense).
In the following example, I开发者_JAVA技巧 create several hundred threads that call repeteadly a single Random object. And when I run it, I sometimes (not always, but nearly) get clearly wrong results. The problem NEVER happens if I uncomment the Synchronized
method annotation.
using System;
using System.Threading;
using System.Runtime.CompilerServices;
namespace testRandom {
class RandTest {
static int NTIMES = 300;
private long ac=0;
public void run() { // ask for random number 'ntimes' and accumulate
for(int i=0;i<NTIMES;i++) {
ac+=Program.getRandInt();
System.Threading.Thread.Sleep(2);
}
}
public double getAv() {
return ac/(double)NTIMES; // average
}
}
class Program
{
static Random random = new Random();
static int MAXVAL = 256;
static int NTREADS = 200;
//[MethodImpl(MethodImplOptions.Synchronized)]
public static int getRandInt() {
return random.Next(MAXVAL+1); // returns a value between 0 and MAXVAL (inclusive)
}
public static void Main(string[] args) {
RandTest[] tests = new RandTest[NTREADS];
Thread[] threads = new Thread[NTREADS];
for(int i=0;i<NTREADS;i++) {
tests[i]= new RandTest();
threads[i] = new Thread(new ThreadStart(tests[i].run));
}
for(int i=0;i<NTREADS;i++) threads[i].Start();
threads[0].Join();
bool alive=true;
while(alive) { // make sure threads are finished
alive = false;
for(int i=0;i<NTREADS;i++) { if(threads[i].IsAlive) alive=true; }
}
double av=0;
for(int i=0;i<NTREADS;i++) av += tests[i].getAv();
av /= NTREADS;
Console.WriteLine("Average:{0, 6:f2} Expected:{1, 6:f2}",av,MAXVAL/2.0);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
An example ouput (with the above values) :
Average: 78.98 Expected:128.00
Press any key to continue . . .
Is this some known issue? Is it incorrect to call a Random object from several threads without sync?
UPDATE: As per answers, the docs state that Random methods are not thread safe - mea culpa, I should have read that. Perhaps I had read that before but didn't think it so important - one could (sloppily) think that, in the rare event of two threads entering the same method concurrently, the worst that could happen is that those calls get wrong results - not a huge deal, if we are not too concerned about random number quality... But the problem is really catastrophic, because the object is left in an inconsistent state, and from that on it returns keeps returning zero - as noted here.
For some strange reason
It's not really strange - Random
is documented not to be thread-safe.
It's a pain, but that's life. See my article on Random for more information, and suggestions for how to have an instance per thread, with guards against starting with the same seed in multiple threads.
The Random
class is not thread safe.
From the docs:
Any instance members are not guaranteed to be thread safe
Instead of synchronizing which will cause all the threads to block, try implementing the ThreadStatic
attribute.
Random isn't guaranteed to be thread safe: http://msdn.microsoft.com/en-us/library/system.random.aspx unless it's public static.
Unfortunately this is correct, one has to be careful when using the Random Class.
Here are two blog posts with more details, comments and code samples on this topic:
- Another issue with the .NET Random class
- Humm … .NET Random class is not Thread Safe?
The worse part of this behaviour is that it just stops working (i.e. once the problem occurs the return value from the 'random.Next....' methods is 0)
精彩评论