comment faire un générateur de nombres aléatoires ac # thread-safe

J’ai une boucle dans mon code

Parallel.For(0, Cnts.MosqPopulation, i => { DoWork() }); 

toutefois, dans la fonction DoWork() , il existe plusieurs appels à un générateur de nombres aléatoires, défini comme suit:

 public static class Utils { public static readonly Random random = new Random(); } 

C’est une instance statique pour qu’elle ne soit semée qu’une seule fois. Et je peux l’utiliser dans tout le code.

Selon MSDN et d’autres threads stackoverflow, ce n’est pas threadsafe. En effet, j’ai parfois remarqué des ruptures de code et le générateur de nombres aléatoires commence à générer tous les zéros (comme indiqué dans la documentation MSDN).

Il existe d’autres threads stackoverflow, mais ils sont plutôt anciens et la mise en œuvre est lente. Je ne peux pas me permettre de perdre du temps à générer les chiffres, car le programme est un calcul scientifique qui exécute des centaines de simulations.

Je n’ai pas travaillé avec .net depuis la version 2.0 et je ne sais pas comment le langage a évolué pour pouvoir créer un GNA rapide, efficace et thread-safe.

Voici les discussions précédentes:

Le thread du générateur de nombres aléatoires C # est-il sécurisé?

Manière correcte d’utiliser Random dans une application multithread

Générateur de nombres aléatoires rapides et sans danger pour le thread pour C #

Remarque: étant donné que j’ai besoin d’une implémentation rapide, je ne peux pas utiliser le RNGCryptoServiceProvider qui est plutôt lent.

Note 2: Je n’ai pas de code de travail minimal. Je ne sais même pas par où commencer, car je ne sais pas comment fonctionne la sécurité des threads, ni une connaissance approfondie du c #. Il semble donc que je demande une solution complète.

En utilisant l’atsortingbut ThreadStatic et un getter personnalisé, vous obtiendrez une seule instance Random par thread. Si cela n’est pas acceptable, utilisez des verrous.

 public static class Utils { [ThreadStatic] private static readonly Random __random; public static Random Random => __random??(__random=new Random()); } 

L’atsortingbut ThreadStatic n’exécute pas l’initialiseur sur chaque thread, vous êtes donc responsable de le faire dans votre accesseur. Pensez également à votre initialiseur de semences, vous pouvez utiliser quelque chose comme:

 new Random((int) ((1+Thread.CurrentThread.ManagedThreadId) * DateTime.UtcNow.Ticks) ) 

Si vous connaissez le nombre de threads que vous exécutez en parallèle, cela peut fonctionner:

 Random rand = new Random(); var randomNums = Enumerable.Range(0, Cnts.MosqPopulation) .Select(_ => rand.Next()).ToList(); Parallel.For(0, Cnts.MosqPopulation, i => { Random localRand = new Random(randomNums[i]); DoWork(); }); 

Je ne sais pas à quel point il serait impossible de distinguer la dissortingbution résultante d’une dissortingbution uniforme.

Je considérerais quelque chose comme ceci:

 private static int _tracker = 0; private static ThreadLocal _random = new ThreadLocal(() => { var seed = (int)(Environment.TickCount & 0xFFFFFF00 | (byte)(Interlocked.Increment(ref _tracker) % 255)); var random = new Random(seed); return random; }); 

Je ne suis pas un grand fan de ThreadStatic ces jours-ci. Nous avons de meilleurs outils que celui utilisant ThreadLocal . Utilisez simplement _random.Value dans votre boucle parallèle et cela vous donnera un nouveau Random par thread.

Il combine une valeur d’incrémentation atomique ainsi que le comportement par défaut de l’utilisation de Environemnt.TickCount . La valeur incrémentielle est là pour résoudre le problème de l’obtention de la même graine par deux Aléatoires. Notez que cette approche ne permet de créer que 255 random. Si vous avez besoin de plus, changez la taille du masque.

Comme vous l’avez déjà noté, ceci n’est pas utilisable à des fins sécurisées.

Vous pouvez hériter de Random pour construire une classe aléatoire thread-safe

 public class ThreadsafeRandom : Random { private readonly object _lock = new object(); public ThreadsafeRandom() : base() { } public ThreadsafeRandom( int Seed ) : base( Seed ) { } public override int Next() { lock ( _lock ) { return base.Next(); } } public override int Next( int maxValue ) { lock ( _lock ) { return base.Next( maxValue ); } } public override int Next( int minValue, int maxValue ) { lock ( _lock ) { return base.Next( minValue, maxValue ); } } public override void NextBytes( byte[ ] buffer ) { lock ( _lock ) { base.NextBytes( buffer ); } } public override double NextDouble() { lock ( _lock ) { return base.NextDouble(); } } } 

et utiliser une instance de cette classe

 public static class Utils { public static readonly Random random = new ThreadsafeRandom(); }