He depurado algún problema con un Paint.Net plugin y me he topado con algún problema con la clase Random, cuando varios hilos llaman a un método desde una sola instancia.Problemas de concurrencia con Random en .Net?
Por alguna extraña razón, parece que si no impido el acceso simultáneo, al sincronizar el método llamado, mi instancia aleatoria comienza a comportarse ... al azar (pero en el mal sentido).
En el siguiente ejemplo, creo varios cientos de hilos que llaman repetidamente un solo objeto aleatorio. Y cuando lo ejecuto, a veces (no siempre, pero casi) obtengo resultados claramente incorrectos. El problema NUNCA ocurre si elimino el comentario de la anotación de método Synchronized
.
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);
}
}
}
Un ejemplo ouput (con los valores anteriores):
Average: 78.98 Expected:128.00
Press any key to continue . . .
Es esto algún problema conocido? ¿Es incorrecto llamar a un objeto aleatorio desde varios hilos sin sincronizar?
ACTUALIZACIÓN: según las respuestas, los documentos indican que los métodos aleatorios no son seguros para subprocesos - mea culpa, debería haberlo leído. Tal vez lo había leído antes, pero no creía que fuera tan importante; uno podría (descuidadamente) pensar que, en el raro caso de que dos hilos ingresen al mismo método al mismo tiempo, lo peor que podría pasar es que esas llamadas obtengan resultados incorrectos, no un gran problema, si no estamos demasiado preocupados por la calidad de los números aleatorios ... Pero el problema es realmente catastrófico, porque el objeto se deja en un estado inconsistente, y de ahí en adelante regresa a cero, como se indica en here.
A menos que la documentación indique el opuesto e puede suponer que cualquier clase admite múltiples subprocesos que acceden a una sola instancia. – CodesInChaos