This question already has answers here:
Lambda variable capture in loop - what happens here? [duplicate]
(3 answers)
Starting tasks inside a loop: how to pass values that can be changed inside the loop? [duplicate]
(1 answer)
Captured variable in a loop in C#
(10 answers)
Closed 2 years ago.
I have asynchronous method which for purpose of this question can be shortened to syntax like this:
public async Task<int> PositiveParallelAnticipation()
{
//some operations
List<List<MatchResult>> ranges = SplitRange(possibilityList, 4);
var tasks = new List<Task<int>>();
for (int i = 0; i < ranges.Count; i++)
{
//some more operations
//!here is the problem!
tasks.Add(Task.Run(() => PositiveAnticipationLoop(new Team(target), currWorkingTeamList, currworkingMatchList, startingPossibility, ranges[i])));
}
int[] results = await Task.WhenAll(tasks);
return results.Max();
}
And when I run it in debug mode it's all good but when I do it without debugging it throws error like this (when trying to get ranges[i]):
Index was outside the bounds of the array."
What am I missing? And btw I don't mutate ranges list and don't increment or decrement i in any operation which is not included here.
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:
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.
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:
For-Loop and LINQ's deferred execution don't play well together
(2 answers)
Closed 7 years ago.
I'm trying to do parallel programming using Task in .Net 4.0 c#.
output of my program is little confusing.
class Program
{
static void Main(string[] args)
{
List<Task> lstTasks = new List<Task>();
for (int i = 0; i < 5; i++)
{
Task tsk = Task.Factory.StartNew(() => DoSomething(i.ToString()));
lstTasks.Add(tsk);
}
Task.WaitAll(lstTasks.ToArray());
Console.WriteLine("Done");
Console.ReadLine();
}
static void DoSomething(string tasKname)
{
Console.WriteLine(tasKname);
System.Threading.Thread.Sleep(10000);
}
}
Output is
5
5
5
5
5
Done.
I'm expecting.
0
1
2
3
4
Done.
where I'm going wrong?
You created a closure when you defined the function () => DoSomething(i.ToString()).
A closure is an anonymous function/lamdba that references some variables defined in the method where the closure was created. In your case, that's variable i.
When this function is executed, it will use the current value of i, not the value that i had when you created it.
You have to be aware that calling Task.Factory.StartNew will not start executing the task immediately. In your case, the tasks started executing after the for loop, so the value of i is 5.
To get the results you expect, use a separate variable in the loop to store the current value of i.
for (int i = 0; i < 5; i++)
{
int k = i;
Task tsk = Task.Factory.StartNew(() => DoSomething(k.ToString()));
lstTasks.Add(tsk);
}
You shouldn't expect the results in any particular order though.
You are accessing a variable that is changing within your loop. Essentially, your foreach runs so quickly, by the time DoSomething runs, i is 5. try this:
for (int i = 0; i < 5; i++)
{
Task tsk = Task.Factory.StartNew(() => DoSomething(i.ToString()));
lstTasks.Add(tsk);
Thread.Sleep(50);
}
and you should see your expected output in the console.
you say I'm expecting 0 1 2 3 4. But you shouldn't. The most important aspect of Tasks is you don't know when they'll complete. For example, when I alter your code to use a Parallel.Foreach():
Parallel.ForEach(Enumerable.Range(0, 5), i =>
{
Task tsk = Task.Factory.StartNew(() => DoSomething(i.ToString()));
lstTasks.Add(tsk);
});
I get the expected numbers, 0 through 4, but in a random order each time i run the code, because we are using Tasks that are all running independently of eachother.
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