When you see the word "random" in the title of the Stack Overflow website, you can basically determine that this is the same basic problem and countless similar problems. This article will show you why randomness causes so many problems and how to solve them.
Stack Overflow (or newsgroup, or mailing list etc) websites usually have the following problems:
I use Random. Next to generate a Random number, but it always gives me the same number. It keeps running, but each time it produces the same number of times.
This is because of the Code:
// Bad code! Do not use! for (int i = 0; i < 100; i++) { Console.WriteLine(GenerateDigit()); } .... static int GenerateDigit() { Random rng = new Random(); // Assume there'd be more logic here really return rng.Next(10); }
So what is the problem with this program?
1. Interpretation
This Random class is not a real Random number generator. It is a pseudo Random number generator. Any Random instance has a certain number of statuses. When you call Next (or NextDouble or NextBytes), it will use this status to return seemingly Random data, change its internal status so that you will get another pseudo-random number in the next call.
All of this is true. If you start a Random instance in the same initial state (available through the seed) and call it using the same sequence method, then you will get the same result.
So what is the problem in our sample code? A new Random instance we are using is also iterated cyclically. The random parameter-less constructor uses the current date and time as the seed-you can usually execute a lot of code before the Internal timer works, and the current date and time will change. Therefore, if we repeat the same seed, we will get the same result again.
2. What can we do for this?
There are many solutions to this problem, some of which are better than others. Let's pick out one of the methods first, because it is different from other methods.
3. Use an encrypted random number generator
. NET has a RandomNumberGenerator class which should be an abstract class derived from all encrypted random number generators. The framework itself comes with a derived class: RNGCryptoServiceProvider. The idea of encrypting a random number generator is that even if it may still be a pseudo-random generator, it is still hard to predict. The built-in implementation requires multiple entropy sources to effectively present "noise" on your computer, which is hard to predict. It can use this noise not only to calculate a seed, but also to let you know the current status when generating the next number, which may not be enough to predict the next result (or those generated ), this depends on the specific implementation. Windows can also take advantage of the randomness of professional hardware resources (such as a piece of hardware to observe the radioactive isotope decay), so that the random number generator is more secure.
Compared to this Random type, if you see (say) 10 results call Random. next (100) and invested a lot of computing resources, you may develop the initial seeds and predict the following results will be... you may also know what the previous results are. If this random number is applied to securities or finance, it would be disastrous. The encrypted Random number generator is generally slower than Random, but it is better at making numbers unpredictable and independent.
In many cases, the performance of the random number generator is not a problem-but there is a problem with an appropriate API. The random number generator is designed to generate random bytes. Comparing the random API, it allows you to request a random integer, a random double, or a group of random bytes. I often find that I need an integer range to obtain a reliable and consistent random byte array. This is not impossible, but at least you may want an adapter class on a random number generator. In most cases, if you can avoid the traps described above, the pseudo-Random is acceptable.
Let's see how this can be done.
4. Use a reusable instance Random
The core of the repair program for "a large number of repeated numbers" is to reuse the same instance Random. This sounds simple... for example, we can change the original code like this:
// Somewhat better code... Random rng = new Random();for (int i = 0; i < 100; i++) { Console.WriteLine(GenerateDigit(rng)); } ...static int GenerateDigit(Random rng) { // Assume there'd be more logic here really return rng.Next(10); }
Now, our loop will print different numbers... But we haven't finished it yet. What happens if you call this code in a fast continuous period? We may still need to create two Random instances using the same seed ...... although each string of numbers will contain different numbers, we can easily obtain two times the same number of strings.
There are two ways to avoid this problem. One way is to use a single instance Random maintained by a static field to be used by every object. In addition, we can push up the instance. Of course, when we finally reach the plan, this will always be able to instantiate a single random element and pass it to any place. This is a good idea (and the dependency it expresses is good), but it won't work completely ...... at least, if your code uses multiple threads, it will cause problems.
5. Thread Security
Random is NOT thread-safe. This is a real pain point, considering how we want to use a single instance in any program. But the fact is that if you use the same instance from multiple threads, it may end with a completely zero internal state, and this instance becomes useless.
Again, there are two ways to solve this problem. One is to still use an instance, and each caller must remember the random number generator they are using and get the lock at the same time. By locking with a package, you can achieve simplified results, but in a highly multithreaded system, you may still waste a lot of time waiting for the lock.
Here we will learn another way-to make each thread have an instance. We need to ensure that when we create an instance, we do not use the same seed repeatedly (for example, we cannot just call a constructor without parameters), but it is relatively simple.
6. One security-driven
Fortunately, the new ThreadLocal <T>. NET4 class makes it easy to write providers in a single instance for each thread. You only need to construct a delegate call for ThreadLocal <T> to obtain the initial value when you are not there. For me, I chose to use a single seed variable and use Environment for initialization. tickCount (just like the parameter's Random constructor), and every increment, we need a new Random number generator time-this is every thread.
The entire class is static and there is only one public method: Random thread acquisition. This is a method, not an attribute. It is mostly convenient: instead of making the classes that require Random numbers depend on Random, they will depend on Func <Random>. If this type is only designed to run in a single thread, it can call the Random of A Single Instance obtained by the delegate and reuse it; if it can use the call delegate from multiple threads each time, it needs a random number generator. This will only create as many instances as possible with threads, each starting with a different seed. When relying on pass passing, we can use a method to convert:
Code below new TypeThatNeedsRandom (RandomProvider. GetThreadRandom:
using System;using System.Threading; public static class RandomProvider { private static int seed = Environment.TickCount; private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref seed)) ); public static Random GetThreadRandom() { return randomWrapper.Value; } }
It's easy, isn't it? This is because all of its concerns are to provide the correct Random instance. It does not care about the method used to call the obtained instance. Code can still abuse this class. Of course, it is easy to do the right thing by storing a random reference and reusing it with multiple threads.
7. Interface Design Problems
One problem persists: This is still not safe. As I mentioned earlier, the most common derived class is RNGCryptoServiceProvider, and a version of a safer random number generator. However, this API is still difficult to use.
If the framework driver has separated the concept of "random source" from the concept "I want to get a random value in a simple way", it is indeed very pleasant. Then we can use a simple API as needed to support a secure or insecure random source. Unfortunately, there is no such method yet. Maybe in future iterations... or a third party will come up with an adapter to replace it. (Unfortunately, it is quite difficult for me to do this well .) You can easily derive random and overwrite examples and the next Byte... but it is unclear how they need to work, and even the Sample may be tricky. Maybe next time...
This is a foreign article that I translated. Address: http://csharpindepth.com/Articles/Chapter12/Random.aspx
Accept criticism and correction, and reject brainless spam.