4

I'm learning about async programming in C#, and written this code to test Task Parallel Library (Console application):

static void Main(string[] args)
{
    Stopwatch sw = new Stopwatch();

    var opr1 = new SlowOperation();
    var opr2 = new SlowOperation();

    //TASK
    Console.WriteLine("Started processing using TASK. Start: {0}", sw.Elapsed);
    sw.Start();

    Task.Factory.StartNew(() => opr1.PerformSlowOperation(1));
    Task.Factory.StartNew(() => opr2.PerformSlowOperation(2));

    Console.WriteLine("Stopped processing using TASK. Stop: {0}", sw.Elapsed);
    sw.Stop();

}

where slow operation is:

public class SlowOperation
{
    public void PerformSlowOperation(int id)
    {
        var rand = new Random();
        double sum = 0;

        for (int i = 0; i < 100000000; i++)
        {
            var number = Convert.ToDouble(rand.Next(100)) / 100;
            sum += number;
        }
        Console.WriteLine("Finished processing operation no. {0}. Final sum calculated is: {1}", id, sum.ToString("0.##"));
    }
}

Could anyone please help me to understand why sum produced by each instance of SlowOperation class is exactly the same?

Bartosz
  • 4,542
  • 11
  • 43
  • 69
  • Why would you start a bunch of tasks and then immediately disable your stopwatch and say they've finished without *actually waiting* for them to finish? – Kirill Shlenskiy Apr 03 '16 at 12:51

2 Answers2

2

Random is seeded based on time at a low resolution. This is a classic issue and, in my mind, an API design error. I think this is already changed in the CoreCLR repo.

new Random().Next() == new Random().Next() is almost always true.

Also note, that 95% of the code in the question has nothing to do with the problem. In the future you can simplify the code yourself until only the random call is left. That allows you to find such problems yourself.

usr
  • 168,620
  • 35
  • 240
  • 369
2

Set with a different seed value in each task. For example:

var rand = new Random(new System.DateTime().Millisecond + id);

Random construtor: https://msdn.microsoft.com/pt-br/library/ctssatww(v=vs.110).aspx

If your application requires different random number sequences, invoke this constructor repeatedly with different seed values.One way to produce a unique seed value is to make it time-dependent.For example, derive the seed value from the system clock.However, the system clock might not have sufficient resolution to provide different invocations of this constructor with a different seed value.

Adriano Godoy
  • 1,488
  • 1
  • 11
  • 21
  • 2
    Calling `new Random(new System.DateTime().Millisecond)` is no different from calling `new Random()`. The underlying is still based on milliseconds. – Enigmativity Apr 03 '16 at 13:24
  • You're right. I gave an unfortunate example, I will edit my answer and remove this Example. – Adriano Godoy Apr 03 '16 at 14:01