Threading / random number problem

M

Mark Rae

Hi,

My R&D department has asked me to look at threading in a Web Service written
in C#, so I came up with the following code:

using System;
using System.ComponentModel;
using System.Threading;
using System.Web.Services;

namespace CWSThreading
{
public class CThreading : System.Web.Services.WebService
{
private double mdThread1;
private double mdThread2;

public CThreading()
{
//CODEGEN: This call is required by the ASP.NET Web Services Designer
InitializeComponent();
}

[WebMethod]
public string TestThreading()
{
string strOutput = "";

Thread thread1 = new Thread(new ThreadStart(RandomNumber1));
thread1.Start();
strOutput += "Result from 1st thread: " + mdThread1.ToString() + "\n";

Thread thread2 = new Thread(new ThreadStart(RandomNumber2));
thread2.Start();
strOutput += "Result from 2nd thread: " + mdThread2.ToString() + "\n";

return strOutput;
}

protected void RandomNumber1()
{
System.Random rndNumber = new Random((int)DateTime.Now.Ticks);
mdThread1 = rndNumber.NextDouble();
}

protected void RandomNumber2()
{
System.Random rndNumber = new Random((int)DateTime.Now.Ticks);
mdThread2 = rndNumber.NextDouble();
}
}
}

This is being called by a Windows app to which a web reference has been
added pointing to the above web service. A simple form with a button runs
the following code:

using TestWSThreading.wsThreading;

private void cmdTestWSThreading_Click(object sender, System.EventArgs e)
{
wsThreading.CThreading wsTestThreading = new CThreading();
string strOutput = wsTestThreading.TestThreading();
MessageBox.Show(strOutput);
wsTestThreading = null;
}


If I set a breakpoint anywhere in the above code which causes me to step
through it, it always returns different random numbers e.g.:

Result from 1st thread: 0.513379168935762
Result from 2nd thread: 0.202799929866008

However, if I remove the breakpoint so that the web service runs "normally",
I always get the following result:

Result from 1st thread: 0
Result from 2nd thread: 0

Any assistance gratefully received.

Best regards,

Mark Rae
 
M

Mark Rae

Sami Vaaraniemi said:
Comments inline:

Thanks for the response.
The main issue here as far as I see is that you start the threads and then
immediately read the variables that the threads are supposed to assign
values. There is no guarantee that the threads execute before you read the
variables. To fix this, you should wait for the threads to complete before
you access the variables:

// start both threads
Thread thread1 = new Thread(new ThreadStart(RandomNumber1));
thread1.Start();
Thread thread2 = new Thread(new ThreadStart(RandomNumber2));
thread2.Start();

// wait for thread1 to complete, read its results
thread1.Join();
strOutput += "Result from 1st thread: " + mdThread1.ToString() + "\n";

// wait for thread2 to complete, read its results
thread2.Join();
strOutput += "Result from 2nd thread: " + mdThread2.ToString() + "\n";

I did as you suggested. Now, I don't get zeroes any more, but now the two
threads always return the same value e.g.

Result from 1st thread: 0.533777334510245
Result from 2nd thread: 0.533777334510245
The results are highly unpredictable unless you do some sort of
synchronization. You're getting 0s because the main thread reads the
variables before the background threads have a chance to execute. Use
Thread.Join to synchronize the threads.

I'm obviously missing something else... :)

Mark
 
S

Steve McLellan

Random's being seeded from the current time, and both your threads are
essentially executing at the same time (give or take) so it's possible
they're getting seeded with the same number. It does seem a bit strange
(since you'd think that Tick would never return the same value even if the
execution times are very similar). That would be my best guess.

Steve
 
M

Morten Wennevik

Considering that

DateTime.Ticks:
"The value of this property is the number of 100-nanosecond intervals that
have elapsed since 12:00 A.M., January 1, 0001."

and 3GHz computers have clock cycles of ~3 nanoseconds it's quite possible
to get a random initiated with the same seed if you do it close enough.
 
M

Mark Rae

Random's being seeded from the current time, and both your threads are
essentially executing at the same time (give or take) so it's possible
they're getting seeded with the same number. It does seem a bit strange
(since you'd think that Tick would never return the same value even if the
execution times are very similar). That would be my best guess.

Thanks for the response. Yes, the reason I was using the code

System.Random rndNumber = new Random((int)DateTime.Now.Ticks);

was in the hope that it would always generate a different number...
 
M

Mark Rae

Thanks for the response.
The reason for the same number being returned is that the .NET Random number
generator is a so-called pseudo-random generator. Given the same seed it
always generates the same sequence of "random" numbers. So the numbers are
not really random (whatever that means), they are just evenly distributed.
You are getting the same random number because the threads happen to
initialize the generator with the same seed value.

I understand.
To ensure unique seeds for the random number generator, you could use e.g.,
the thread ID (AppDomain.GetCurrentThreadId or Thread.GetHashCode()) as the
seed. You could also combine DateTime.Now.Ticks with the thread ID
somehow.

Yes - I changed part of the code to:

thread1.Join();
strOutput += "Result from 1st thread: " + Convert.ToString((mdThread1 /
thread1.GetHashCode()) * 1000) + "\n";
thread2.Join();
strOutput += "Result from 2nd thread: " + Convert.ToString((mdThread2 /
thread2.GetHashCode()) * 1000) + "\n";

and it now produces different (albeit very similar!) numbers every time.

Job done - thanks for your help.

Mark
 
M

Mark Rae

Considering that

DateTime.Ticks:
"The value of this property is the number of 100-nanosecond intervals that
have elapsed since 12:00 A.M., January 1, 0001."

and 3GHz computers have clock cycles of ~3 nanoseconds it's quite possible
to get a random initiated with the same seed if you do it close enough.

You're right - I hadn't considered that! It's been a while since I
encountered a problem because my development machine was too fast... :)
 
S

Sami Vaaraniemi

Comments inline:

Mark Rae said:
Hi,

My R&D department has asked me to look at threading in a Web Service written
in C#, so I came up with the following code:

using System;
using System.ComponentModel;
using System.Threading;
using System.Web.Services;

namespace CWSThreading
{
public class CThreading : System.Web.Services.WebService
{
private double mdThread1;
private double mdThread2;

public CThreading()
{
//CODEGEN: This call is required by the ASP.NET Web Services Designer
InitializeComponent();
}

[WebMethod]
public string TestThreading()
{
string strOutput = "";

Thread thread1 = new Thread(new ThreadStart(RandomNumber1));
thread1.Start();
strOutput += "Result from 1st thread: " + mdThread1.ToString() + "\n";

Thread thread2 = new Thread(new ThreadStart(RandomNumber2));
thread2.Start();
strOutput += "Result from 2nd thread: " + mdThread2.ToString() + "\n";

return strOutput;
}

The main issue here as far as I see is that you start the threads and then
immediately read the variables that the threads are supposed to assign
values. There is no guarantee that the threads execute before you read the
variables. To fix this, you should wait for the threads to complete before
you access the variables:

// start both threads
Thread thread1 = new Thread(new ThreadStart(RandomNumber1));
thread1.Start();
Thread thread2 = new Thread(new ThreadStart(RandomNumber2));
thread2.Start();

// wait for thread1 to complete, read its results
thread1.Join();
strOutput += "Result from 1st thread: " + mdThread1.ToString() + "\n";

// wait for thread2 to complete, read its results
thread2.Join();
strOutput += "Result from 2nd thread: " + mdThread2.ToString() + "\n";

If I set a breakpoint anywhere in the above code which causes me to step
through it, it always returns different random numbers e.g.:

Result from 1st thread: 0.513379168935762
Result from 2nd thread: 0.202799929866008

However, if I remove the breakpoint so that the web service runs "normally",
I always get the following result:

Result from 1st thread: 0
Result from 2nd thread: 0

Any assistance gratefully received.

The results are highly unpredictable unless you do some sort of
synchronization. You're getting 0s because the main thread reads the
variables before the background threads have a chance to execute. Use
Thread.Join to synchronize the threads.

Sami
www.capehill.net
 
S

Sami Vaaraniemi

Mark Rae said:
I did as you suggested. Now, I don't get zeroes any more, but now the two
threads always return the same value e.g.

Result from 1st thread: 0.533777334510245
Result from 2nd thread: 0.533777334510245


I'm obviously missing something else... :)

The reason for the same number being returned is that the .NET Random number
generator is a so-called pseudo-random generator. Given the same seed it
always generates the same sequence of "random" numbers. So the numbers are
not really random (whatever that means), they are just evenly distributed.
You are getting the same random number because the threads happen to
initialize the generator with the same seed value.

To ensure unique seeds for the random number generator, you could use e.g.,
the thread ID (AppDomain.GetCurrentThreadId or Thread.GetHashCode()) as the
seed. You could also combine DateTime.Now.Ticks with the thread ID somehow.

Sami
www.capehill.net
 
J

Jon Skeet [C# MVP]

Mark Rae said:
Yes - I changed part of the code to:

thread1.Join();
strOutput += "Result from 1st thread: " + Convert.ToString((mdThread1 /
thread1.GetHashCode()) * 1000) + "\n";
thread2.Join();
strOutput += "Result from 2nd thread: " + Convert.ToString((mdThread2 /
thread2.GetHashCode()) * 1000) + "\n";

and it now produces different (albeit very similar!) numbers every time.

That's not changed the seed at all - that's just divided the *result*
by the hash code.

Instead, it would be better to have *one* random number generator, and
serialize access to it. You only need to have one seed then, and using
the current time for that is reasonable.
 
M

Mark Rae

That's not changed the seed at all - that's just divided the *result*
by the hash code.
Correct.

Instead, it would be better to have *one* random number generator, and
serialize access to it. You only need to have one seed then, and using
the current time for that is reasonable.

Why would this be better?
 
J

Jon Skeet [C# MVP]

Mark Rae said:
Why would this be better?

Well, *anything* would be better than just dividing by the hash.
However, seeding the random number generator from something other than
the thread hash would be good, as the thread hash is relatively
predictable.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top