Click here to Skip to main content
15,886,763 members
Articles / Programming Languages / C#

Random Number in MultiThreading

Rate me:
Please Sign up or sign in to vote.
4.76/5 (8 votes)
4 Mar 2015CPOL4 min read 36.7K   197   11   4
Random number in MultiThreading

Random Number

Random number class in .NET Framework is used for generating random numbers. Random number class allows creating random number instance by following two constructors:

  1. Random() – It makes use of system clock as seed value and creates instance.

    As random makes use of System clock as input when creating two instances as below:

    C#
     Random rand = new Random();
    Random rand1 = new Random();
    

    It use the same system clock input so output of the above when doing below code...

    C#
    Console.WriteLine(rand.Next());
           Console.WriteLine(rand1.Next());
    

    ...generates the same random number. Example both lines of code write 10 on console.

  2. Random(int32 seed) – It makes use of input integer value and creates instance.

    As random makes use of integer value as input when creating two instances with different inputs...

    C#
    Random rand = new Random(30);
                  Random rand1 = new Random(40);

    ...so output of the above when writing below code:

    C#
    Console.WriteLine(rand.Next());
            Console.WriteLine(rand1.Next());
    

    ...generate different random number.

 

So the above two ways of creating random instance show that seed value plays an important role in creating random number instance.

Random In Multi-Threading

Following are the lines of code that are used as simulator, i.e., following lines of code are used to generate which are used by other application.

C#
static void Main(string[] args)
        {
            Program program = new Program();

            char[] publish = new char[] { 'r', 'v'};

            List<Task> tasks = new List<Task>();
            while (true)
            {
                Console.WriteLine("Press any key to publish new data and 'n' to exit");
                char rchar = Convert.ToChar(Console.Read());
                Console.ReadLine();
                if (rchar == 'n')
                    break;

                foreach (char c in publish)
                {
                    if (c == 'r')
                        tasks.Add(Task.Run(() =>program.PublishRateOrVolume(c)));
                    else if (c == 'v')
                        tasks.Add(Task.Run(() =>program.PublishRateOrVolume(c)));
                }

                try
                {
                    Task.WaitAll(tasks.ToArray());
                    foreach (Task<string> t in tasks)
                        Console.WriteLine(t.Result);
                }
                catch (AggregateException ae)
                {
                    Console.WriteLine(ae.ToString());
                }
                tasks.Clear();
            }
            tasks = null;
        }

The above lines of code create two tasks in that one task generates rate date and another task generates volume data for publishing data. Things to note in the above code are both the task calling the same method but with the different input value ‘r’ (rate) and ‘v’ (volume).

C#
private string PublishRateOrVolume(char publishChar)
{
    StringBuilder sb = new StringBuilder();
    char[] inputchar = new char[] { '1', '2'};
    string clientName = string.Empty;

    var random = new Random();

    try
    {
        foreach (char c in inputchar)
        {
            if (c == '1')
                clientName = "Test 1";
            if (c == '2')
                clientName = "Test 2";

            var data = random.NextDouble() * random.Next(0, 500);

            if (publishChar == 'v')
                sb.AppendLine("VOLUME Data Publish by Client :" + clientName + " Volume :" + data);
            else if (publishChar == 'r')
               sb.AppendLine("RATE Data Publish by Client :" + clientName + " Rate :" + data);
        }
        return sb.ToString();
    }
    finally
    {
        random = null;
        inputchar = null;
        sb = null;
    }
}

The above code function is called by different task with respected argument to generate volume and rate data. Each task generates volume and rate data for two clients (test 1 and test 2). Things to note in code function creates Random class instance for generating random data.

The above line of code generates the following output:

Image 1

Problem with the output is that the program generates the same data for volume and rate for each client which is not expected, expected result is it generates different random values of rate and value for each client.

What is the Issue?

The issue with the above code is random number instance gets created by two different tasks almost with the same seed value, as the seed value for both random instance is the same, it generate the same values. It’s already discussed above in random class constructor example.

Following is the solution to the problem of random number in multithreading environment.

  1. Add delay between creating two tasks:

    As random instance makes use of system clock value, adding delay between creation of two tasks gives different seed value to default random class instance. So the code for this is as follows:

    C#
    foreach (char c in publish)
                  {
                      if (c == 'r')
                          tasks.Add(Task.Run(() =>program.PublishRateOrVolume(c)));
                      else if (c == 'v')
                          tasks.Add(Task.Run(() =>program.PublishRateOrVolume(c)));
                      Thread.Sleep(50);
            }
    

    With the Thread.Sleep, small delay gets added between creating of task and as there is delay, two instances of random number receive different seed.

    But the problem with this solution is that it requires to add delay, so if there are more than two tasks, it will create a problem, also it causes a problem in mission critical application where time is important.

  2. Use only one instance of random class:

    According to this solution, only one instance of random number class gets created and shared between multiple threads. So the code is:

    C#
                static void Main(string[] args)
           {
            Random random = new Random();
            // code as it is
            foreach (char c in publish)
                    {
                        if (c == 'r')
                            tasks.Add(Task.Run(() => program.PublishRateOrVolume(c, random)));
                        else if (c == 'v')
                            tasks.Add(Task.Run(() => program.PublishRateOrVolume(c, random)));
                    }
              }
                 //code as it is
            }
    private string PublishRateOrVolume(char publishChar, Random random)
           {
            //code as it is
    }

    As in code, one random instance is created in main method and same instance is used by both tasks to generate random number.

    But the problem with this solution is one random number instance is shared between two tasks and random number class is not thread safe.

    As random number instance is not thread safe when two threads call next() method at the same time, it will generate 0 as output and then afterwards random number generates 0 and is not useful. To check, try the below example:

    C#
           Random rand = new Random();
    
           Parallel.For(0, 1000000, (i, loop) =>
           {
               if (rand.Next() == 0) loop.Stop();
    });
    

    To avoid problems, make use of lock when accessing random instance via its methods like next(), nextDouble().

    C#
            object lockobj = new object();
    lock (lockobj)
                        {
                            data= rand.NextDouble() * rand.Next(0, 500);
                        }

    It resolves the issue, but the problem is lock statement slows down application when there are more number of threads.

  3. Make use of ThreadLocal<T> with the different seed value:

    This solution makes use of ThreadLocal<T> class, it allows creating local variable for each thread/task. Read more about ThreadLocal class at https://msdn.microsoft.com/en-us/library/dd642243%28v=vs.110%29.aspx.

    So the code with ThreadLocal<T> is as below:

    C#
    private string PublishRateOrVolume(char publishChar)
           {
    var random = new ThreadLocal<Random>(() => new        Random(Guid.NewGuid().GetHashCode()));
    //code as it is
    var data = random.Value.NextDouble() * random.Value.Next(0, 500);
    //code as it is
    }

    In code, random instance created using ThreadLocal<T> class means each task receives its own local instance of Random class.

    Another thing to note in code is seed value passed when creating Random instance. Seed value is HashCode value of the Guid instance, i.e., each time new instance receives new seed value.

Conclusion

Random class is not thread safe and above are three solutions to make it thread safe but out of three, the third solution is perfect and fits as a thread safe solution.

Please find the attached code which has all three solutions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India

Microsoft C# MVP (12-13)



Hey, I am Pranay Rana, working as a Team Leadin MNC. Web development in Asp.Net with C# and MS sql server are the experience tools that I have had for the past 5.5 years now.

For me def. of programming is : Programming is something that you do once and that get used by multiple for many years

You can visit my blog


StackOverFlow - http://stackoverflow.com/users/314488/pranay
My CV :- http://careers.stackoverflow.com/pranayamr

Awards:



Comments and Discussions

 
GeneralSome inconsistency in approaches Pin
OneWinsto14-Jul-15 13:05
OneWinsto14-Jul-15 13:05 
QuestionThreadsafe and secure random generator Pin
erichlund20-Feb-15 3:33
erichlund20-Feb-15 3:33 
QuestionSharing Random between threads is a bad idea. Pin
Philippe Mori18-Feb-15 6:29
Philippe Mori18-Feb-15 6:29 
It would make it impossible to test the program with know seeds as you generally don't have control on the order of which thread will call the random generator on next iteration.
Thus you never want to use that approach.
Philippe Mori

QuestionSimple implementation for the seeding in threads Pin
Thomas Maierhofer (Tom)18-Feb-15 1:54
Thomas Maierhofer (Tom)18-Feb-15 1:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.