Task in a loop starts with wrong (last) index as parameter [duplicate] - c#

In this example, I'm attempting to pass by value, but the reference is passed instead.
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(() => new PhoneJobTest(i));
t.Start();
}
This can be remedied like so:
for (int i = 0; i < 10; i++)
{
int jobNum = i;
Thread t = new Thread(() => new PhoneJobTest(jobNum));
t.Start();
}
What's is going on here? Why does the original example pass the reference?

Well, that's just how C# works. The lambda expression in your statement constructs a lexical closure, which stores a single reference to i that persists even after the loop has concluded.
To remedy it, you can do just the thing that you did.
Feel free to read more on this particular issue all around the Web; my choice would be Eric Lippert's discussion here.

This is easier to understand if you look at what happens, in terms of scope:
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(() => new PhoneJobTest(i);
t.Start();
}
Basically translates to something very close to this:
int i = 0;
while (i < 10)
{
Thread t = new Thread(() => new PhoneJobTest(i);
t.Start();
i++;
}
When you use a lambda expression, and it uses a variable declared outside of the lambda (in your case, i), the compiler creates something called a closure - a temporary class that "wraps" the i variable up and provides it to the delegate generated by the lambda.
The closure is constructed at the same level as the variable (i), so in your case:
int i = 0;
ClosureClass = new ClosureClass(ref i); // Defined here! (of course, not called this)
while (i < 10)
{
Thread t = new Thread(() => new PhoneJobTest(i);
t.Start();
i++;
}
Because of this, each Thread gets the same closure defined.
When you rework your loop to use a temporary, the closure is generated at that level instead:
for (int i = 0; i < 10; i++)
{
int jobNum = i;
ClosureClass = new ClosureClass(ref jobNum); // Defined here!
Thread t = new Thread(() => new PhoneJobTest(jobNum);
t.Start();
}
Now, each Thread gets its own instance, and everything works properly.

Short answer: closures. Long answer given here (among other places): Differing behavior when starting a thread: ParameterizedThreadStart vs. Anonymous Delegate. Why does it matter?

You definitely want to read Eric Lippert's "Closing over the loop variable considered harmful":
Part 1
Part 2
In Short: The behavior you see is exactly how C# works.

It happens because of the way C# passes parameters to a lambda. It wraps the variable access in a class which is created during compilation, and exposes it as a field to the lambda body.

When using an anonymous delegate or a lambda expression a closure is created so outside variables can be referenced. When the closure is created the stack (value) variables are promoted to the heap.
One way to avoid this is to start the thread with a ParameterizedThreadStart delegate. E.G.:
static void Main()
{
for (int i = 0; i < 10; i++)
{
bool flag = false;
var parameterizedThread = new Thread(ParameterizedDisplayIt);
parameterizedThread.Start(flag);
flag = true;
}
Console.ReadKey();
}
private static void ParameterizedDisplayIt(object flag)
{
Console.WriteLine("Param:{0}", flag);
}
Coincidentally I ran into this concept just yesterday: Link

Related

C# Initialize an array of functions with a fixed index doesn't work [duplicate]

I've been learning C#, and I'm trying to understand lambdas. In this sample below, it prints out 10 ten times.
class Program
{
delegate void Action();
static void Main(string[] args)
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 10; ++i )
actions.Add(()=>Console.WriteLine(i));
foreach (Action a in actions)
a();
}
}
Obviously, the generated class behind the lambda is storing a reference or pointer to the int i variable, and is assigning a new value to the same reference every time the loop iterates. Is there a way to force the lamda to grab a copy instead, like the C++0x syntax
[&](){ ... } // Capture by reference
vs.
[=](){ ... } // Capture copies
What the compiler is doing is pulling your lambda and any variables captured by the lambda into a compiler generated nested class.
After compilation your example looks a lot like this:
class Program
{
delegate void Action();
static void Main(string[] args)
{
List<Action> actions = new List<Action>();
DisplayClass1 displayClass1 = new DisplayClass1();
for (displayClass1.i = 0; displayClass1.i < 10; ++displayClass1.i )
actions.Add(new Action(displayClass1.Lambda));
foreach (Action a in actions)
a();
}
class DisplayClass1
{
int i;
void Lambda()
{
Console.WriteLine(i);
}
}
}
By making a copy within the for loop, the compiler generates new objects in each iteration, like so:
for (int i = 0; i < 10; ++i)
{
DisplayClass1 displayClass1 = new DisplayClass1();
displayClass1.i = i;
actions.Add(new Action(displayClass1.Lambda));
}
The only solution I've been able to find is to make a local copy first:
for (int i = 0; i < 10; ++i)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy));
}
But I'm having trouble understanding why putting a copy inside the for-loop is any different than having the lambda capture i.
The only solution is to make a local copy and reference that within the lambda. All variables in C# (and VB.Net) when accessed in a closure will have reference semantics vs. copy/value semantics. There is no way to change this behavior in either language.
Note: It doesn't actually compile as a reference. The compiler hoists the variable into a closure class and redirects accesses of "i" into a field "i" inside the given closure class. It's often easier to think of it as reference semantics though.
Remember that lambda expressions are really only syntactic sugar for anonymous methods.
That being said, what you are really looking for is how anonymous methods use local variables in a parent scope.
Here's a link describing this. http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx#4

C# Parallel.Invoke: When are delegates actually created? [duplicate]

I met an interesting issue about C#. I have code like below.
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
actions.Add(() => variable * 2);
++ variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
I expect it to output 0, 2, 4, 6, 8. However, it actually outputs five 10s.
It seems that it is due to all actions referring to one captured variable. As a result, when they get invoked, they all have same output.
Is there a way to work round this limit to have each action instance have its own captured variable?
Yes - take a copy of the variable inside the loop:
while (variable < 5)
{
int copy = variable;
actions.Add(() => copy * 2);
++ variable;
}
You can think of it as if the C# compiler creates a "new" local variable every time it hits the variable declaration. In fact it'll create appropriate new closure objects, and it gets complicated (in terms of implementation) if you refer to variables in multiple scopes, but it works :)
Note that a more common occurrence of this problem is using for or foreach:
for (int i=0; i < 10; i++) // Just one variable
foreach (string x in foo) // And again, despite how it reads out loud
See section 7.14.4.2 of the C# 3.0 spec for more details of this, and my article on closures has more examples too.
Note that as of the C# 5 compiler and beyond (even when specifying an earlier version of C#), the behavior of foreach changed so you no longer need to make local copy. See this answer for more details.
I believe what you are experiencing is something known as Closure http://en.wikipedia.org/wiki/Closure_(computer_science). Your lamba has a reference to a variable which is scoped outside the function itself. Your lamba is not interpreted until you invoke it and once it is it will get the value the variable has at execution time.
Behind the scenes, the compiler is generating a class that represents the closure for your method call. It uses that single instance of the closure class for each iteration of the loop. The code looks something like this, which makes it easier to see why the bug happens:
void Main()
{
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
var closure = new CompilerGeneratedClosure();
Func<int> anonymousMethodAction = null;
while (closure.variable < 5)
{
if(anonymousMethodAction == null)
anonymousMethodAction = new Func<int>(closure.YourAnonymousMethod);
//we're re-adding the same function
actions.Add(anonymousMethodAction);
++closure.variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
}
class CompilerGeneratedClosure
{
public int variable;
public int YourAnonymousMethod()
{
return this.variable * 2;
}
}
This isn't actually the compiled code from your sample, but I've examined my own code and this looks very much like what the compiler would actually generate.
The way around this is to store the value you need in a proxy variable, and have that variable get captured.
I.E.
while( variable < 5 )
{
int copy = variable;
actions.Add( () => copy * 2 );
++variable;
}
This has nothing to do with loops.
This behavior is triggered because you use a lambda expression () => variable * 2 where the outer scoped variable not actually defined in the lambda's inner scope.
Lambda expressions (in C#3+, as well as anonymous methods in C#2) still create actual methods. Passing variables to these methods involve some dilemmas (pass by value? pass by reference? C# goes with by reference - but this opens another problem where the reference can outlive the actual variable). What C# does to resolve all these dilemmas is to create a new helper class ("closure") with fields corresponding to the local variables used in the lambda expressions, and methods corresponding to the actual lambda methods. Any changes to variable in your code is actually translated to change in that ClosureClass.variable
So your while loop keeps updating the ClosureClass.variable until it reaches 10, then you for loops executes the actions, which all operate on the same ClosureClass.variable.
To get your expected result, you need to create a separation between the loop variable, and the variable that is being closured. You can do this by introducing another variable, i.e.:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
var t = variable; // now t will be closured (i.e. replaced by a field in the new class)
actions.Add(() => t * 2);
++variable; // changing variable won't affect the closured variable t
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
You could also move the closure to another method to create this separation:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
actions.Add(Mult(variable));
++variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
You can implement Mult as a lambda expression (implicit closure)
static Func<int> Mult(int i)
{
return () => i * 2;
}
or with an actual helper class:
public class Helper
{
public int _i;
public Helper(int i)
{
_i = i;
}
public int Method()
{
return _i * 2;
}
}
static Func<int> Mult(int i)
{
Helper help = new Helper(i);
return help.Method;
}
In any case, "Closures" are NOT a concept related to loops, but rather to anonymous methods / lambda expressions use of local scoped variables - although some incautious use of loops demonstrate closures traps.
Yes you need to scope variable within the loop and pass it to the lambda that way:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
int variable1 = variable;
actions.Add(() => variable1 * 2);
++variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
Console.ReadLine();
The same situation is happening in multi-threading (C#, .NET 4.0].
See the following code:
Purpose is to print 1,2,3,4,5 in order.
for (int counter = 1; counter <= 5; counter++)
{
new Thread (() => Console.Write (counter)).Start();
}
The output is interesting! (It might be like 21334...)
The only solution is to use local variables.
for (int counter = 1; counter <= 5; counter++)
{
int localVar= counter;
new Thread (() => Console.Write (localVar)).Start();
}
for (int n=0; n < 10; n++) //forloop syntax
foreach (string item in foo) foreach syntax
It is called the closure problem,
simply use a copy variable, and it's done.
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
int i = variable;
actions.Add(() => i * 2);
++ variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
Since no one here directly quoted ECMA-334:
10.4.4.10 For statements
Definite assignment checking for a for-statement of the form:
for (for-initializer; for-condition; for-iterator) embedded-statement
is done as if the statement were written:
{
for-initializer;
while (for-condition) {
embedded-statement;
LLoop: for-iterator;
}
}
Further on in the spec,
12.16.6.3 Instantiation of local variables
A local variable is considered to be instantiated when execution enters the scope of the variable.
[Example: For example, when the following method is invoked, the local variable x is instantiated and initialized three times—once for each iteration of the loop.
static void F() {
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}
However, moving the declaration of x outside the loop results in a single instantiation of x:
static void F() {
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
...
}
}
end example]
When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent.
[Example: The example
using System;
delegate void D();
class Test{
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}
static void Main() {
foreach (D d in F()) d();
}
}
produces the output:
1
3
5
However, when the declaration of x is moved outside the loop:
static D[] F() {
D[] result = new D[3];
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}
the output is:
5
5
5
Note that the compiler is permitted (but not required) to optimize the three instantiations into a single delegate instance (§11.7.2).
If a for-loop declares an iteration variable, that variable itself is considered to be declared outside of the loop.
[Example: Thus, if the example is changed to capture the iteration variable itself:
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
result[i] = () => { Console.WriteLine(i); };
}
return result;
}
only one instance of the iteration variable is captured, which produces the output:
3
3
3
end example]
Oh yea, I guess it should be mentioned that in C++ this problem doesn't occur because you can choose if the variable is captured by value or by reference (see: Lambda capture).

Create Task in loop but all tasks take the same parameter [duplicate]

I met an interesting issue about C#. I have code like below.
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
actions.Add(() => variable * 2);
++ variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
I expect it to output 0, 2, 4, 6, 8. However, it actually outputs five 10s.
It seems that it is due to all actions referring to one captured variable. As a result, when they get invoked, they all have same output.
Is there a way to work round this limit to have each action instance have its own captured variable?
Yes - take a copy of the variable inside the loop:
while (variable < 5)
{
int copy = variable;
actions.Add(() => copy * 2);
++ variable;
}
You can think of it as if the C# compiler creates a "new" local variable every time it hits the variable declaration. In fact it'll create appropriate new closure objects, and it gets complicated (in terms of implementation) if you refer to variables in multiple scopes, but it works :)
Note that a more common occurrence of this problem is using for or foreach:
for (int i=0; i < 10; i++) // Just one variable
foreach (string x in foo) // And again, despite how it reads out loud
See section 7.14.4.2 of the C# 3.0 spec for more details of this, and my article on closures has more examples too.
Note that as of the C# 5 compiler and beyond (even when specifying an earlier version of C#), the behavior of foreach changed so you no longer need to make local copy. See this answer for more details.
I believe what you are experiencing is something known as Closure http://en.wikipedia.org/wiki/Closure_(computer_science). Your lamba has a reference to a variable which is scoped outside the function itself. Your lamba is not interpreted until you invoke it and once it is it will get the value the variable has at execution time.
Behind the scenes, the compiler is generating a class that represents the closure for your method call. It uses that single instance of the closure class for each iteration of the loop. The code looks something like this, which makes it easier to see why the bug happens:
void Main()
{
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
var closure = new CompilerGeneratedClosure();
Func<int> anonymousMethodAction = null;
while (closure.variable < 5)
{
if(anonymousMethodAction == null)
anonymousMethodAction = new Func<int>(closure.YourAnonymousMethod);
//we're re-adding the same function
actions.Add(anonymousMethodAction);
++closure.variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
}
class CompilerGeneratedClosure
{
public int variable;
public int YourAnonymousMethod()
{
return this.variable * 2;
}
}
This isn't actually the compiled code from your sample, but I've examined my own code and this looks very much like what the compiler would actually generate.
The way around this is to store the value you need in a proxy variable, and have that variable get captured.
I.E.
while( variable < 5 )
{
int copy = variable;
actions.Add( () => copy * 2 );
++variable;
}
This has nothing to do with loops.
This behavior is triggered because you use a lambda expression () => variable * 2 where the outer scoped variable not actually defined in the lambda's inner scope.
Lambda expressions (in C#3+, as well as anonymous methods in C#2) still create actual methods. Passing variables to these methods involve some dilemmas (pass by value? pass by reference? C# goes with by reference - but this opens another problem where the reference can outlive the actual variable). What C# does to resolve all these dilemmas is to create a new helper class ("closure") with fields corresponding to the local variables used in the lambda expressions, and methods corresponding to the actual lambda methods. Any changes to variable in your code is actually translated to change in that ClosureClass.variable
So your while loop keeps updating the ClosureClass.variable until it reaches 10, then you for loops executes the actions, which all operate on the same ClosureClass.variable.
To get your expected result, you need to create a separation between the loop variable, and the variable that is being closured. You can do this by introducing another variable, i.e.:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
var t = variable; // now t will be closured (i.e. replaced by a field in the new class)
actions.Add(() => t * 2);
++variable; // changing variable won't affect the closured variable t
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
You could also move the closure to another method to create this separation:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
actions.Add(Mult(variable));
++variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
You can implement Mult as a lambda expression (implicit closure)
static Func<int> Mult(int i)
{
return () => i * 2;
}
or with an actual helper class:
public class Helper
{
public int _i;
public Helper(int i)
{
_i = i;
}
public int Method()
{
return _i * 2;
}
}
static Func<int> Mult(int i)
{
Helper help = new Helper(i);
return help.Method;
}
In any case, "Closures" are NOT a concept related to loops, but rather to anonymous methods / lambda expressions use of local scoped variables - although some incautious use of loops demonstrate closures traps.
Yes you need to scope variable within the loop and pass it to the lambda that way:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
int variable1 = variable;
actions.Add(() => variable1 * 2);
++variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
Console.ReadLine();
The same situation is happening in multi-threading (C#, .NET 4.0].
See the following code:
Purpose is to print 1,2,3,4,5 in order.
for (int counter = 1; counter <= 5; counter++)
{
new Thread (() => Console.Write (counter)).Start();
}
The output is interesting! (It might be like 21334...)
The only solution is to use local variables.
for (int counter = 1; counter <= 5; counter++)
{
int localVar= counter;
new Thread (() => Console.Write (localVar)).Start();
}
for (int n=0; n < 10; n++) //forloop syntax
foreach (string item in foo) foreach syntax
It is called the closure problem,
simply use a copy variable, and it's done.
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
int i = variable;
actions.Add(() => i * 2);
++ variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
Since no one here directly quoted ECMA-334:
10.4.4.10 For statements
Definite assignment checking for a for-statement of the form:
for (for-initializer; for-condition; for-iterator) embedded-statement
is done as if the statement were written:
{
for-initializer;
while (for-condition) {
embedded-statement;
LLoop: for-iterator;
}
}
Further on in the spec,
12.16.6.3 Instantiation of local variables
A local variable is considered to be instantiated when execution enters the scope of the variable.
[Example: For example, when the following method is invoked, the local variable x is instantiated and initialized three times—once for each iteration of the loop.
static void F() {
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}
However, moving the declaration of x outside the loop results in a single instantiation of x:
static void F() {
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
...
}
}
end example]
When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent.
[Example: The example
using System;
delegate void D();
class Test{
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}
static void Main() {
foreach (D d in F()) d();
}
}
produces the output:
1
3
5
However, when the declaration of x is moved outside the loop:
static D[] F() {
D[] result = new D[3];
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}
the output is:
5
5
5
Note that the compiler is permitted (but not required) to optimize the three instantiations into a single delegate instance (§11.7.2).
If a for-loop declares an iteration variable, that variable itself is considered to be declared outside of the loop.
[Example: Thus, if the example is changed to capture the iteration variable itself:
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
result[i] = () => { Console.WriteLine(i); };
}
return result;
}
only one instance of the iteration variable is captured, which produces the output:
3
3
3
end example]
Oh yea, I guess it should be mentioned that in C++ this problem doesn't occur because you can choose if the variable is captured by value or by reference (see: Lambda capture).

C# Winforms: Why does 'i' output as 2 both times [duplicate]

I'm using Task to process multiple requests in parallel and passing a different parameter to each task but it seems all the tasks takes one final parameter and execute the method using that.
Below is the sample code. I was expecting output as:
0 1 2 3 4 5 6 ..99
but I get:
100 100 100 ..10 .
May be before print method is called, i's value is already 100 but shouldn't each method print the parameter passed to it? Why would print method takes the final value of i?
class Program
{
static void Main(string[] args)
{
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
t[i] = Task.Factory.StartNew(() => print(i));
}
Task.WaitAll(t);
Console.WriteLine("complete");
Console.ReadLine();
}
private static void print(object i)
{
Console.WriteLine((int)i);
}
}
You're a victim of a closure. A simplest fix to this issue is:
for (int i = 0; i < 100; i++)
{
int v = i;
t[i] = Task.Factory.StartNew(() => print(v));
}
You can find more detailed explanations here and here.
Problems occur when you reference a variable without considering
its scope.
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
t[i] = Task.Factory.StartNew(() => print(i));
}
Task.WaitAll(t);
You might think that, your task will consider each i th value in it's execution. But that won't happen since Task execution start sometime in future. That means, the variable i is shared by all the closures created by the steps of the for loop. By the time the tasks start, the value of the single, shared variable i. This is why all task print same ith value.
The solution is to introduce an additional temporary variable in
the appropriate scope.
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
var temp=i;
t[i] = Task.Factory.StartNew(() => print(temp));
}
Task.WaitAll(t);
This version prints the numbers 1, 2, 3, 4..100 in an arbitrary order, but each
number will be printed. The reason is that the variable tmp is declared
within the block scope of the for loop’s body. This causes a new
variable named tmp to be instantiated with each iteration of the for
loop. (In contrast, all iterations of the for loop share a single instance
of the variable i.)
For info, another fix here is to use the state parameter of the Task API, i.e.
t[i] = Task.Factory.StartNew(state => print((int)state), i);
Unfortunately, since the state parameter is object, this still boxes the value, but it avoids needing an entire closure and separate delegate per call (with the code shown immediately above, the compiler is smart enough to use a single delegate instance for all the iterations; this is not possible if you add a local variable (like the v in BartoszKP's answer), as the target is the closure instance, and that then varies per iteration).

Why would Task object not use parameter passed to it?

I'm using Task to process multiple requests in parallel and passing a different parameter to each task but it seems all the tasks takes one final parameter and execute the method using that.
Below is the sample code. I was expecting output as:
0 1 2 3 4 5 6 ..99
but I get:
100 100 100 ..10 .
May be before print method is called, i's value is already 100 but shouldn't each method print the parameter passed to it? Why would print method takes the final value of i?
class Program
{
static void Main(string[] args)
{
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
t[i] = Task.Factory.StartNew(() => print(i));
}
Task.WaitAll(t);
Console.WriteLine("complete");
Console.ReadLine();
}
private static void print(object i)
{
Console.WriteLine((int)i);
}
}
You're a victim of a closure. A simplest fix to this issue is:
for (int i = 0; i < 100; i++)
{
int v = i;
t[i] = Task.Factory.StartNew(() => print(v));
}
You can find more detailed explanations here and here.
Problems occur when you reference a variable without considering
its scope.
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
t[i] = Task.Factory.StartNew(() => print(i));
}
Task.WaitAll(t);
You might think that, your task will consider each i th value in it's execution. But that won't happen since Task execution start sometime in future. That means, the variable i is shared by all the closures created by the steps of the for loop. By the time the tasks start, the value of the single, shared variable i. This is why all task print same ith value.
The solution is to introduce an additional temporary variable in
the appropriate scope.
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
var temp=i;
t[i] = Task.Factory.StartNew(() => print(temp));
}
Task.WaitAll(t);
This version prints the numbers 1, 2, 3, 4..100 in an arbitrary order, but each
number will be printed. The reason is that the variable tmp is declared
within the block scope of the for loop’s body. This causes a new
variable named tmp to be instantiated with each iteration of the for
loop. (In contrast, all iterations of the for loop share a single instance
of the variable i.)
For info, another fix here is to use the state parameter of the Task API, i.e.
t[i] = Task.Factory.StartNew(state => print((int)state), i);
Unfortunately, since the state parameter is object, this still boxes the value, but it avoids needing an entire closure and separate delegate per call (with the code shown immediately above, the compiler is smart enough to use a single delegate instance for all the iterations; this is not possible if you add a local variable (like the v in BartoszKP's answer), as the target is the closure instance, and that then varies per iteration).

Categories