tags:

views:

4193

answers:

4

This is a very strange problem.

Here is my code:

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

How I call it:

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

Problem:

If I step that loop with the debugger during runtime I get different values(which is what I want). However, if I put a breakpoint two lines below that code,all members of the "mac" array have equal value.

Why does that happen?

+67  A: 

Every time you do new Random() it is initialized using the clock. This means that in a tight loop you get the same value lots of times. You should keep a single Random instance and keep using Next on the same 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);
    }
}
Marc Gravell
+1 for correct. Shoot, you beat me to it. :)
Greg D
When I grow up. I want to answer Qs as fast as you. LOL. +1
Jose Basilio
I'm not sure this is the answer in this case, since the question states that the function is working properly when there is no breakpoints near this segment of the code. Of course I agree that he should use one instance of Random object, however I don't think this answers his question.
Rekreativc
@Rekreativc - no, re-read it: it is working **only** when there are break-points, which cause a delay and thus provide different seeds. Without the break-points, everything is equal, i.e. not random, i.e. broken.
Marc Gravell
That's right.@Marc Gravell,you might surpass Jon Skeet :)
John
@Marc,could you tell me what the lock(syncLock) does?
John
@John - it is hard enough just trying to keep him in sight (mostly as a dot in the middle-distance).
Marc Gravell
Certainly; it synchronizes access, so that if multiple threads are calling your static "RandomNumber" method, they don't trip over each-other and cause an error. Only one thread will ever be inside the "lock" statement at once, since they are all sharing a single lock object (`syncLock`).
Marc Gravell
As a general rule, all static methods should be made thread-safe, since it is hard to guarantee that multiple threads won't call it at the same time. It is **not** usually necessary to make *instance* (i.e. non-static) methods thread-safe.
Marc Gravell
@Marc Gravell - Ah I see it now. I apologise.
Rekreativc
Hmm, on the contrary, I would believe that instance methods need more synchronization because most of the time they use the external state of the object they are defined in, whilst, the static methods, use local variables (stack based) that are not shared between different (possibly parallel) invocation of the method. So I think in this particular case, the sync may be ok (because it uses a shared resource), but I wouldn't make that a general, best practice, rule.
Florin Sabau
@Florin - there is no difference re "stack based" between the two. Static fields are just as much "external state", and will **absolutely** be shared between callers. With instances, there is a good chance that different threads have different instances (a common pattern). With statics, it is *guaranteed* that they all share (not including [ThreadStatic]).
Marc Gravell
I wish I could up vote 1000 times!!! You are a genius!!
TheMachineCharmer
This is great, works good.
sumit_programmer
A: 

I would rather use the following class to generate random numbers:



byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new   System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);
fARcRY
I'm not one of the down-voters, but note that standard PNRG do serve a genuine need - i.e. to be able to repeatably reproduce a sequence from a known seed. Sometimes the sheer *cost* of a true cryptographic RNG is too much. And sometimes a crypto RNG is necessary. Horses for courses, so to speak.
Marc Gravell
+1  A: 

1) As Marc Gravell said, try to use ONE random-generator. It's always cool to add this to the constructor: System.Environment.TickCount.

2) One tip. Let's say you want to create 100 objects and suppose each of them should have its-own random-generator (handy if you calculate LOADS of random numbers in a very short period of time). If you would do this in a loop (generation of 100 objects), you could do this like that (to assure fully-randomness):

int inMyRandSeed;

for(int i=0;i<100;i++)
{
   inMyRandSeed = System.Environment.TickCount + i;
   .
   .
   .
   myNewObject = new MyNewObject(inMyRandSeed);  
   .
   .
   .
}

// Usage: Random m_rndGen = new Random(inMyRandSeed);

Cheers.

I would move System.Environment.TickCount out of the loop. If it ticks over while you are iterating then you will have two items initialized to the same seed. Another option would be to combine the tickcount an i differently (e.g. System.Environment.TickCount<<8 + i)
Dolphin
If I understand correctly: do you mean, it could happen, that "System.Environment.TickCount + i" could result the SAME value?
EDIT: Of course, no need to have TickCount inside the loop. My bad :).
+1  A: 

Mark's solution can be quite expensive since it needs to synchronize everytime.

We can get around the need for synchronization by using the thread-specific storage pattern:


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);
    }
}

Measure the two implementations and you should see a significant difference.

Hans Malherbe
Locks are very cheap when they aren't contested... and even if contested I would expect the "now do something with the number" code to dwarf the cost of the lock in most interesting scenarios.
Marc Gravell
Agreed, this solves the locking problem, but isn't this still a highly complicated solution to a trivial problem: that you need to write ''two'' lines of code to generate a random number instead of one. Is this really worth it to save reading one simple line of code?
Evgeny