4

I need to use random class to generate random numbers in a multi threaded application inside public static function. How can i achieve it. Currently the function below is working very well but it is not very fast when compared to random class. So i need to modify the function below and make it work with random class while thousands of concurrent calls are happening to that class. if i use random it uses same seed for every call i suppose and the randomization is being very bad. my current class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;

public static class GenerateRandomValue
{
    static RNGCryptoServiceProvider Gen = new RNGCryptoServiceProvider();

    public static int GenerateRandomValueDefault(int irRandValRange)//default min val 1
    {
        if (irRandValRange == 0)
            irRandValRange = 1;
        byte[] randomNumber = new byte[4]; // 4 bytes per Int32
        Gen.GetBytes(randomNumber);
        return Math.Abs(BitConverter.ToInt32(randomNumber, 0) % irRandValRange) + 1;
    }

    public static int GenerateRandomValueMin(int irRandValRange, int irMinValue)
    {
        byte[] randomNumber = new byte[4]; // 4 bytes per Int32
        Gen.GetBytes(randomNumber);
        return BitConverter.ToInt32(randomNumber, 0) % irRandValRange + irMinValue;
    }
}

Another function which seems pretty good and thread safe

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;

public static class GenerateRandomValue
{
    private static Random seedGenerator = new Random();

    private static ThreadLocal<Random> random = new ThreadLocal<Random>(SeededRandomFactory);

    private static Random SeededRandomFactory()
    {
        lock(seedGenerator)
            return new Random(seedGenerator.Next());
    }

    public static int GenerateRandomValueMin(int irRandValRange, int irMinValue)
    {
        return random.Value.Next(irMinValue, irRandValRange);
    }
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Furkan Gözükara
  • 22,964
  • 77
  • 205
  • 342
  • 9
    Any reason the `Random` class is not good enough? – Oded Oct 26 '11 at 19:38
  • random class is totally useless when compared to this one ^^ i also tried it and i can clearly say 1/10 random when compared to this one. maybe even worse. – Furkan Gözükara Oct 26 '11 at 19:40
  • 1
    Useless? In what way? If you are only interested in speed, it is fine. You didn't specify anything else. Please do. – Oded Oct 26 '11 at 19:42
  • Please define "random perfection". – Oded Oct 26 '11 at 19:44
  • 2
    I suggest reading this: http://www.random.org/randomness/ – Oded Oct 26 '11 at 19:46
  • generate random 1 million number and compare how random it is – Furkan Gözükara Oct 26 '11 at 19:50
  • 3
    @MontsterMMORPG: If you explain what you actually mean by "random perfection" -- ie, what your actual requirements are -- then you might get some more pertinent answers. – LukeH Oct 26 '11 at 20:01
  • i found my problem. i was using random class directly in my asp.net application. so thousands of concurrent calls were happening. someone from msdn just pointed that out. so how should i modify the function above to work correctly when using random class at asp.net ? i mean the problem was seed. it was generating all same values. – Furkan Gözükara Oct 26 '11 at 20:21
  • 1
    @MonsterMMORPG You should edit your post and explain clearly and concisely what you are trying to do, how you are currently doing it, exactly how it is not working, and what your requirements are. – Scott Chamberlain Oct 26 '11 at 20:24
  • i added another function the question. it is using random class and it is thread safe. you can also check it. – Furkan Gözükara Oct 27 '11 at 00:11

4 Answers4

3

You didn't specify any constraints other than speed, so I would think that Random should do.

// Field in the class
Random rand = new Random();

// Inside a method:
int randomValue = rand.Next(); // Random positive integer returned
int randomValue = rand.Next(max); // Random under max
int randomValue = rand.Next(min, max); // Random in range
Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • Oded is right. The RNGCryptoServiceProvider is used to produce cryptographically strong random numbers. If you don't have this requirement, stick to the Random class. – Henning Krause Oct 26 '11 at 19:40
  • random is 1/10 random of this class. I also need to maintain random perfection. – Furkan Gözükara Oct 26 '11 at 19:40
  • @MonsterMMORPG - Why do you need "random perfection"? – Oded Oct 26 '11 at 19:43
  • Oded, look at the OP's handle. If this is for any kind of online game (or betting) the RNG is advisable. – H H Oct 26 '11 at 19:49
  • @HenkHolterman - Fair enough, though he seems reluctant to explain the constraints required by the random function he needs (apart from speed). – Oded Oct 26 '11 at 19:51
  • yes this is online game. i used at first random method but it was not random at all :D – Furkan Gözükara Oct 26 '11 at 19:51
  • i also edited first question. i have to keep at least this function random quality. – Furkan Gözükara Oct 26 '11 at 19:53
  • i found my problem. i was using random class directly in my asp.net application. so thousands of concurrent calls were happening. someone from msdn just pointed that out. so how should i modify the function above to work correctly when using random class at asp.net ? i mean the problem was seed. it was generating all same values. – Furkan Gözükara Oct 26 '11 at 20:21
3

What you need is a better way to start the seeding in your ASP.NET application, the quality of Random should be fine using the below method.

public static int GenerateRandomValueDefault(int irRandValRange)//default min val 1
{
    return GenerateRandomValueMin(irRandValRange, 1);
}

public static int GenerateRandomValueMin(int irRandValRange, int irMinValue)
{
    Random rand = GetRandom();
    return rand.GetNext(irMinValue,irRandValRange)
}

//This is a global random number generator, it is only used to provide the seed for the local RNG's.
private static Random GlobalRandom = new Random();

private static Random GetRandom()
{
    if (HttpContext.Current.Session["RNG"] == null)
    {
        //This lock is only hit the very first time the users Session state is used, every time after that it should use the cached local copy without taking the lock.
        lock(GlobalRandom)
        {
            //We use the Global RNG for seed instead of the default because it uses time to seed by default, and if two people get a new Random() within the same time-slice they will have the same seed. This prevents that from happening.
            HttpContext.Current.Session["RNG"] = new Random(GlobalRandom.Next());
        }
    }
    //Return the cached/new RNG.
    return (Random)HttpContext.Current.Session["RNG"];
}

You have one instance of a global RNG that does lock, however this is only hit when a new session state is generated, after that the session uses only it's local copy. You will get very good performance at run time with a slight load on the first page load per person as it generates one number from the global store.

You can modify this to suit your needs but it gives you the general idea, but it gives you the general idea.


Per Henk Holterman's suggestion, here is a lock less solution that may be faster and does not use HttpState.

private static int SeedCounter = 0;
private readonly object SeedInitLock = new Object();

private static Random GetRandom()
{
    //Do init the first time this function is ever called.
    if(SeedCounter == -1)
    {
        //The first time the function is called everyone will try to update SeedCounter, but only the first 
        //thread to complete it will be the value everyone uses.
        Random initRNG = new Random();
        Interlocked.CompareExchange(ref SeedCounter, initRNG.Next(), -1);

    }
    else if (SeedCounter < 0)
    {
        //Because Interlocked.Increment wraps the value to int.MinValue and Random(int) will take the absolute
        //value of the seed, we skip all of the negitive numbers and go to 0.
        Interlocked.CompareExchange(ref SeedCounter, 0, int.MinValue);
    }

    int tempSeed = Interlocked.Increment(ref SeedCounter);
    if (tempSeed < 0)
    {
        //If tempSeed is negative we hit a edge case where SeedCounter wrapped around. We just call the function
        //again so we do not reuse a seed that was just used.
        return GetRandom();
    }

    return new Random(tempSeed);
}
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • thanks a lot for your great answer. do you think would there be significant performance difference what your function and my current function. and your way would also be 100% random right ? not affected from multiple users – Furkan Gözükara Oct 26 '11 at 21:34
  • For performance you will just have to run both and see what the results are, but I suspect it will be faster, but the only way to know for sure is test. But for your use case, this will generate totally different random numbers per user even if they both connect to the server at the exact same time. – Scott Chamberlain Oct 26 '11 at 21:38
  • +1 for taking control over the seed. But storing it in the session seems overkill (and is Random Serializable?). An instance per request would probably do. And then something is needed for the seed. – H H Oct 26 '11 at 21:40
  • One possible solution would be to have just one `Random` instance per thread. In .NET4 or newer you could use a static [`ThreadLocal`](http://msdn.microsoft.com/en-us/library/dd642243.aspx); in older versions you could use a static `Random` marked with the [`ThreadStatic`](http://msdn.microsoft.com/en-us/library/system.threadstaticattribute.aspx) attribute. – LukeH Oct 26 '11 at 21:58
  • @LukeH How are you going to seed those ThreadLocal instances? If you just use the default constructor you will have the same problem as the OP with two people using the same time slice for the seed. – Scott Chamberlain Oct 26 '11 at 22:34
  • @HenkHolterman I posted a new solution based off of your suggestion. but the [MSDN says Random is Serializable](http://msdn.microsoft.com/en-us/library/system.random.aspx). – Scott Chamberlain Oct 26 '11 at 22:35
  • @Scott: Here you go: `static int _seed = Environment.TickCount; static ThreadLocal _rng = new ThreadLocal(() => new Random(Interlocked.Increment(ref _seed)));` – LukeH Oct 26 '11 at 23:35
  • i added another function the question. it is using random class and it is thread safe. you can also check it. – Furkan Gözükara Oct 27 '11 at 00:10
  • @LukeH What about when `_seed == int.MaxValue` Your next generator will be identical to the last one (and the one you made 2 ago will be identical with the one after that) because Random takes the absolute value of the seed. If you are cycling through a lot of short lived threads this could become a issue. – Scott Chamberlain Oct 27 '11 at 01:43
  • @Scott: You could use something like `() => new Random(Interlocked.Increment(ref _seed) & 0x7FFFFFFF)` as your factory func. Obviously, there'll still be a "pattern" to the seeds, but nothing that a casual observer will notice. (And if that sort of thing is a concern then the `Random` class itself would be completely unsuitable anyway.) – LukeH Oct 27 '11 at 08:27
  • @Scott SeedCounter should be initially set at -1 to enter the initialization section. – tiktock Aug 31 '14 at 18:15
0

I would suggest System.Web.Security.Membership.GeneratePassword() method.

string generated = System.Web.Security.Membership.GeneratePassword(
                   10, // maximum length
                   3)  // number of non-ASCII characters.
Mostafa
  • 3,002
  • 10
  • 52
  • 79
0

Using Random with a good seed is suppose to be "performance wise" better. But do you need the performance ?

Check this blog for metrics : http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx There is a hint at the end about seeding with the provider.

Take it with a grain of salt though. You will have to redo the tests to be sure.

Khan
  • 516
  • 11
  • 24
  • Random class is not random at all when compared to this. you can generate random numbers between 1 and 1000 and generate 1 million number and compare. – Furkan Gözükara Oct 26 '11 at 19:52
  • i found my problem. i was using random class directly in my asp.net application. so thousands of concurrent calls were happening. someone from msdn just pointed that out. so how should i modify the function above to work correctly when using random class at asp.net ? i mean the problem was seed. it was generating all same values. – Furkan Gözükara Oct 26 '11 at 20:21