This question already has answers here:
Closures in C# - initating a thread in a function call with a value type
(3 answers)
Closed 3 years ago.
I have a problem with my multi threading app.
I have an array with 4 Threads in it.
Here is how I initialize them:
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(delegate () { ThreadMethod(i); });
}
But after my first test(s) I showed that all four threads run with 3 as passed value.
I chagend it to
for (int i = 0; i < threads.Length; i++)
{
int id = i;
threads[i] = new Thread(delegate () { ThreadMethod(id); });
}
and it worked but for me it looks like there must be a better way to pass my i to my thread.
Am I right with my guess and if I am, how should I pass i?
Thank you!
The closure uses local variables -- in a for loop the variables declared for the loop are not local they have to be inside the block ({}) for that. That is why the 2nd example works.
The delegate keyword is what creates the closure and it looks for local variables to include in the closure. When it finds id it includes it. When it doesn't it references the global (outside the closure) value of i and at the time of running i has a value of 3.
Related
This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 1 year ago.
I've been trying to get familiar with the Tasks library in C# and parallelism in general. My newMethodForThreads() method gives an error indicating the the thread is trying to access a file already opened by another thread.
In trying to debug this, it seems that the for loop in createTasks() is passing in the same arguments into newMethodForThreads() in different iterations. It also seems that some iterations of i don't get passed into the task either, and are skipped entirely. Does anyone understand what's going on?
public static List<Task> createTasks(int x)
{
List<Task> taskList = new List<Task>();
for (int i = 1; i <= x; i++)
{
taskList.Add(Task.Factory.StartNew(() => newMethodForThreads(i)));
}
return taskList;
}
public static void newMethodForThreads(int i)
{
File.WriteAllLines($"C:\\Users\\my_username\\Desktop\\Shenanigans\\Threadedfile{i}.txt", list);
Console.WriteLine($"File Threadedfile{i}.txt finished.");
}
The problem is that the task has a reference to the integer. So it will use the value the integer has when the taks is started and not when the taks is created.
To fix it assign the integer to a local variable just before the task is created.
for (int i = 1; i <= x; i++)
{
var localValue = i;
taskList.Add(Task.Factory.StartNew(() => newMethodForThreads(localValue)));
}
This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed last month.
I have this code and do not understand why the out put is 22! I am afraid it should be 01!
can anyone explain what happens? if the list store a method with a parameter, so the parameters should be 0 and 1 respectively!
List<Action> list = new List<Action>();
for (int i = 0; i < 2; i++)
{
list.Add(() => Console.Write(i));
}
foreach (var it in list)
{
it();
}
It is Closure (1, 2).
In your case Console.Write(i) will use value of i in the moment of action call. You firstly increment i in for loop then in second loop you call every action in the list. In the moment of call of every action i has value 2 - so, you get 22 as output.
To get expected result you should create local copy of i and use it:
for (int i = 0; i < 2; i++)
{
var temp = i;
list.Add(() => Console.Write(temp));
}
Addition to Roma Doskoch's anwser, another approach is to avoid for.
var list = Enumerable
.Range(0, 2)
.Select<int, Action>(i => () => Console.Write(i));
Closures capture variables, not values.
In your code, the closure captures the variable i, not whatever value happens to be stored in i on each iteration. When you invoke the action, the variable i has a value of 2 (because the loop has finished) and therefore 2 will be printed out twice.
In order to avoid this, as other answers already point out, you need to create a new variable every time around as a workaround to not being able to capture values; if you declare a new variable on every iteration then the result of capturing the variable is equivalent to capturing the value because you won't be changing it on the next iteration.
This question already has answers here:
How can I capture the value of an outer variable inside a lambda expression?
(4 answers)
Closed 6 years ago.
What is going on here? This loop most of the time just prints this:
10101010101010101010
sometimes this:
51010101010101010101
and when i debug it, it prints in order
0123456789
class Program
{
static void Main (string[] args)
{
for ( int i = 0; i < 10; i++)
{
Task.Run(( ) => Console.Write(i));
}
Console.Read();
}
}
If you have ReSharper installed it puts a little squiggle underneath the i:
with the note:
Access to modified closure
The JetBrains site gives this explanation:
This may appear to be correct but, in actual fact, only the last value of str variable will be used whenever any button is clicked. The reason for this is that foreach unrolls into a while loop, but the iteration variable is defined outside this loop. This means that by the time you show the message box, the value of str may have already been iterated to the last value in the strings collection.
(obviously their example uses a string not an int).
It "works" under debug because other things are going on and the code isn't being executed the same as it would in release.
The solution is to use a local variable:
for ( int i = 0; i < 10; i++)
{
int local = i;
Task.Run(( ) => Console.Write(local));
}
But even then it's not guaranteed to execute in the order you expect. I just tested this and got he result:
0436215897
So each value was processed, but in indeterminate order.
This question already has answers here:
Using the iterator variable of foreach loop in a lambda expression - why fails?
(3 answers)
Closed 8 years ago.
I got this piece of code,
delegate void Printer();
static void Main(string[] args)
{
List<Printer> printers = new List<Printer>();
for (int i = 0; i < 10; i++)
{
printers.Add(delegate { Console.WriteLine(i); });
}
foreach (Printer printer in printers)
{
printer();
}
Console.ReadLine();
}
Here the output is '10' for ten times.
The scope of i is with in the for loop. But while we retrieve in out side that we are still getting value from i.
How is it possible?
You have modified closure. Try this:
for (int i = 0; i < 10; i++)
{
int ii = i;
printers.Add(delegate { Console.WriteLine(ii); });
}
When you use in your anonymous method access the the variable in you local scope it creates closure.
The code in the delegate is not run until it is called, which happens in the second loop. It then refers to the i which was defined within the scope of the first loop, but with it's current value - and since the first loop has been completed already, i will be 10 each time.
I believe each of the delegates you create are given the same scope as the first loop, if that makes sense. This means that each i has it's delegate as it's scope, and since each delegate is defined within the scope of the first loop, each i will also have the loop as it's scope, even if the delegate logic is called outside that scope, as in your example.
Since i is valid throughout / across several iterations of the loop, it gets updated, and is always 10 by the time the delegates get called.
This explains why the following works as a fix:
for(int i = 0; i < 10; i++)
{
var localVar = i; // Only valid within a single iteration of the loop!
printers.Add(delegate { Console.WriteLine(localVar); });
}
Let's unroll the loop:
int i=0;
printers.Add(delegate { Console.WriteLine(i); })
i=1;
printers.Add(delegate { Console.WriteLine(i); })
...
i=10;
printers.Add(delegate { Console.WriteLine(i); })
As you can see the i variable is captured within the delegate, and the delegate itself is not run until the loop ends, and the variable has achieved the last value (10).
A simple workaround is to assign the loop variable to a local helper variable
for (int i = 0; i < 10; i++)
{
var index = i;
printers.Add(delegate { Console.WriteLine(index); });
}
As for the scope issue, any captured variables have their scope (and lifetime) extended. A variable used within a lambda/delegate will not be garbage collected until the delegate itself goes out of scope - which can be a problem for large objects. Specifically, section, 7.15.5.1 of the C# 5 Specification states:
When an outer variable is referenced by an anonymous function, the
outer variable is said to have been captured by the anonymous
function. Ordinarily, the lifetime of a local variable is limited to
execution of the block or statement with which it is associated
(ยง5.1.7). However, the lifetime of a captured outer variable is
extended at least until the delegate or expression tree created from
the anonymous function becomes eligible for garbage collection.
Each delegate is only invoked in the foreach, after the for loop. By this time, the variable i captured by the closure is already at its final value, viz 10. You can solve like so:
for (int i = 0; i < 10; i++)
{
var cache = i;
printers.Add(delegate { Console.WriteLine(cache); });
}
This question already has answers here:
Variable scope confusion in C#
(4 answers)
Closed 9 years ago.
Is there any practical difference (speed, memory) between these two ways of declaring (introducing) variables? Which one is the better way?
Example 1:
for (int i = 0; i < myObjects.Length; i++)
{
MyObject m = myObjects[i];
}
Example 2:
MyObject m = null;
for (int i = 0; i < myObjects.Length; i++)
{
m = myObjects[i];
}
Thanks.
Example 1 makes it so that m is only "alive", in memory, within the scope of the for loop.
Example 2 makes it so that m stays occupying memory after the for loop finishes executing.
I would go with example 1, this is why:
Declaring variables inside or outside of a loop (I won't really tell you, in detail, to force you to read the link.)
Performance-wise both are compiled to the same IL, so there's no difference.
The first one is better because you are meaning to have a new object in each iteration. Also there is no need to have an object if the loop condition fails