What is the difference between:
new Thread(new ThreadStart(SomeFunc))
and:
new Thread( delegate() { SomeFunc();} )
This code gives strange outputs on my computer:
public class A
{
int Num;
public A(int num)
{
Num = num;
}
public void DoObj(object obj)
{
Console.Write(Num);
}
public void Do()
{
Console.Write(Num);
}
}
/////// in void main()
for (int i = 0; i < 10; i++)
{
(new Thread(new ThreadStart((new A(i)).Do))).Start(); // Line 1
(new Thread(new ThreadStart(delegate() { (new A(i)).Do(); }))).Start(); // Line 2
(new Thread(delegate() { (new A(i)).Do(); })).Start(); // Line 3
}
If only Line 1 is executed the output is something like:
0 2 3 1 5 6 4 7 8 9
which is ok but if Line 2 or 3 is executed, output is:
3 3 3 5 5 7 7 9 9 10
There are some multiple numbers and a 10 which is quite strange that the loop is never run with the number 10. What is the trick behind these?
Thanks.
To answer your first point,
delegate() { SomeFunc();}
creates a function that callsSomeFunc()
, whereas not usingdelegate()
simply uses theSomeFunc
function directly as theThreadStart
method.In your second question, you're running into the implementation details of C# anonymous functions. All three references to
i
refer to the samei
, which is incremented three times. You've got a race condition between the three functions that meani
can be incremented several times before the started threads are run.With the delegate, you are capturing
i
.The difference is that with
new ThreadStart((new A(i)).Do))
, you are creating a new instance ofA
in thefor
loop withi
as a parameter. That means that at that point, the value ofi
is taken and send to the constructor. The delegate you are sending is thus not of the creation ofA
, but you are actually sending the a delegate of theDo
method of the instance ofA
to the constructor.However, with
delegate() { (new A(i)).Do(); })
(both of them), you are sending a reference ofi
to the thread.The thread then takes some time to start and meanwhile, the
for
loop goes on. By the timei
is used in the delegate (i.e. the thread has started), thefor
loop has moved on to3
and that's what you see. The same goes for the second and third thread. The three threads are started but wait for the starting thread to complete some work. Then the created threads kick in (thread 1, 2 and 3) and they do their work. The Windows goes back to the thread with thefor
loop and goes on to start thread 4 and 5.Some reading material:
'When is the constructor for object A called?' helps answer the question.
new ThreadStart((new A(i)).Do))
When this line of code is executed - the constructor is called and a reference to the
.Do
function on the newly created object A is kept by the ThreadStart delegate.In Line 2 and 3 you are using an anonymous delegate (introduced in C# 2.0).
The contents of the anonymous delegate is not executed until the delegate is invoked; in this case by the thread when it is assigned a time slice to do so.
The variable i is only declared once at the start of the for loop, and the delegate contents has a reference to it (delegates will do that) - when the code is executed it must grab the value of i at the time of execution.
This explains the value 10. i has a value of 10 when the loop has finished executing. If one of the threads executes after the loop has finished, it will output a 10.
To avoid the multiple numbers issue you could create a local copy of the loop variable. The delegate will keep a reference to its own version of icopy;