748 votes

Générateur de nombres aléatoires seulement de générer un nombre aléatoire

J'ai la fonction suivante:

//Function to get random number
public static int RandomNumber(int min, int max)
{
    Random random = new Random();
    return random.Next(min, max);
}

Comment je l'appelle:

byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
    mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);

Si je l'étape en boucle avec le débogueur au cours de l'exécution j'obtiens des valeurs différentes (c'est ce que je veux). Cependant, si j'ai mis un point d'arrêt en deux lignes au-dessous de ce code, tous les membres de la "mac" tableau ont la même valeur.

Pourquoi est-ce arrivé?

1022voto

Marc Gravell Points 482669

Chaque fois que vous effectuez new Random() il est initialisé à l'aide de l'horloge. Cela signifie que dans une boucle serrée, vous obtenez la même valeur beaucoup de temps. Vous devriez garder un seul Random exemple et continuer à l'utiliser Next sur la même instance.

//Function to get random number
private static readonly Random random = new Random();
private static readonly object syncLock = new object();
public static int RandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return random.Next(min, max);
    }
}

Edit (voir les commentaires): pourquoi avons-nous besoin d'un lock ici?

Fondamentalement, Next , un changement de l'état interne de l' Random de l'instance. Si nous faisons cela en même temps à partir de plusieurs threads, vous pourriez dire "nous avons juste fait le résultat encore plus aléatoire", mais ce que nous sommes réellement en train de faire est potentiellement casser la mise en œuvre interne, et nous pourrions aussi commencer à obtenir le même nombre de threads différents, ce qui pourrait être un problème - et peut-être pas. La garantie de ce qui se passe en interne est le plus gros problème, cependant, depuis Random ne pas faire toutes les garanties de fil de sécurité. Il y a donc deux approches valables:

  • synchroniser, de sorte que nous n'avons pas accès à la fois à partir de différents threads
  • utiliser différents Random cas par thread

peut-être bien; mais la mutation d'un seul exemple de plusieurs appelants en même temps c'est juste des ennuis.

L' lock réalise la première (et plus simple) de ces approches; cependant, une autre approche pourrait être:

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());

c'est ensuite par thread, de sorte que vous n'avez pas besoin de synchroniser.

115voto

Phil Points 509

Les bonnes pratiques que vous utilisez une statique de la classe d'aide que vous pouvez utiliser dans votre application

public static class StaticRandom
{
    private static int seed;

    private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    static StaticRandom()
    {
        seed = Environment.TickCount;
    }

    public static Random Instance { get { return threadLocal.Value; } }
}

Ensuite, vous pouvez l'appeler à l'aide

StaticRandom.Instance.Next(1, 100);

61voto

Hans Malherbe Points 1426

Marque de la solution peut être assez cher puisqu'il doit se synchroniser à chaque fois.

Nous pouvons l'obtenir autour de la nécessité pour la synchronisation en utilisant le thread spécifique de la structure de stockage:


public class RandomNumber : IRandomNumber
{
    private static readonly Random Global = new Random();
    [ThreadStatic] private static Random _local;

    public int Next(int max)
    {
        var localBuffer = _local;
        if (localBuffer == null) 
        {
            int seed;
            lock(Global) seed = Global.Next();
            localBuffer = new Random(seed);
            _local = localBuffer;
        }
        return localBuffer.Next(max);
    }
}

Mesurer les deux implémentations et vous devriez voir une différence significative.

38voto

nawfal Points 13500

Ma réponse ici:

Juste réitérant la bonne solution:

namespace mySpace
{
    public static class Util
    {
        private static rnd = new Random();
        public static int GetRandom()
        {
            return rnd.Next();
        }
    }
}

De sorte que vous pouvez appeler:

var i = Util.GetRandom();

tout au long de.

Si vous avez strictement besoin d'un vrai apatrides statique méthode pour générer des nombres aléatoires, vous pouvez compter sur un Guid.

public static class Util
{
    public static int GetRandom()
    {
        return Guid.NewGuid().GetHashCode();
    }
}

Ça va être un petit peu plus lent, mais peut être beaucoup plus aléatoire qu' Random.Next, au moins à partir de mon expérience.

Mais pas:

new Random(Guid.NewGuid().GetHashCode()).Next();

Inutile de création de l'objet va rendre plus lent, surtout en vertu d'une boucle.

Et jamais:

new Random().Next();

Non seulement c'est plus lent (à l'intérieur d'une boucle), son entropie est... eh bien pas vraiment bon selon moi..

25voto

fARcRY Points 1391

Je préfère utiliser la classe suivante pour générer des nombres aléatoires:

byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X