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.
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)));
}
In the preparation for a C# exam at university I found the following multiple choice question:
Client applications call your library by passing a set of operations
to perform. Your library must ensure that system resources are most
effectively used. Jobs may be scheduled in any order, but your
librarymust log the position of each operation. You have declared this
code:
public IEnumerable<Task> Execute(Action[] jobs)
{
var tasks = new Task[jobs.Length];
for (var i = 0; i < jobs.Length; i++)
{
/* COMPLETION NEEDED */
}
return tasks;
}
public void RunJob(Action job, int index)
{
// implementation omitted
}
Complete the method by inserting code in the for loop. Choose the
correct answer.
1.)
tasks[i] = new Task((idx) => RunJob(jobs[(int)idx], (int)idx), i);
tasks[i].Start();
2.)
tasks[i] = new Task(() => RunJob(jobs[i], i));
tasks[i].Start();
3.)
tasks[i] = Task.Run(() => RunJob(jobs[i], i));
I have opted for answer 3 since Task.Run() queues the specified work on the thread pool and returns a Task object that represents the work.
But the correct answer was 1, using the Task(Action, Object) constructor. The explanation says the following:
In answer 1, the second argument to the constructor is passed as the
only argument to the Action delegate. The current value of the
i variable is captured when the value is boxed and passed to the Task
constructor.
Answer 2 and 3 use a lambda expression that captures the i variable
from the enclosing method. The lambda expression will probably return
the final value of i, in this case 10, before the operating system
preempts the current thread and begins every task delegate created by
the loop. The exact value cannot be determined because the OS
schedules thread execution based on many factors external to your
program.
While I perfectly understand the explanation of answer 1, I don't get the point in the explanations for answer 2 and 3. Why would the lambda expression return the final value?
In options 2 and 3 lambda captures original i variable used in for loop. It's not guaranteed when tasks will be run on thread pool. So possible behavior: for loop is finished, i=10 and then tasks are started to execute. So all of them will use i=10.
Similar behavior you can see here:
void Do()
{
var actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
actions.Add(() => Console.WriteLine(i));
}
//actions executed after loop is finished
foreach(var a in actions)
{
a();
}
}
Output is:
3
3
3
You can fix it like this:
for (int i = 0; i < 3; i++)
{
var local = i;
actions.Add(() => Console.WriteLine(local));
}
This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Captured Closure (Loop Variable) in C# 5.0
(1 answer)
C# Closures, why is the loopvariable captured by reference?
(6 answers)
How closure in c# works when using lambda expressions?
(3 answers)
Closed 3 years ago.
I can't make heads or tails of the following behaviour (please see comments in code)
for (int i = 0; i < 1000; i++)
{
string debugString = String.Concat("Inside Action ", i);
Action newAction = new Action(() => { System.Console.WriteLine(debugString); });
Task.Run(newAction); // outputs: 0, 1, 2, 3 (not always exactly in this order, but this is understandable because of async execution)
Action newAction2 = new Action(() => { System.Console.WriteLine("Inside Action2 " + i); });
Task.Run(newAction2); // outputs: 1000, 1000, 1000, 1000, 1000 (...which is the value of i *after* the iteration, why?? "i" is a primitive int?!)
}
Console.ReadKey();
I don't understand the second behaviour. The variable i should be assigned to the Action as an Instance as the Action gets created. It looks like a pointer to i is created which has the value 1000 (last value of iteration) at the time the task is run...
Please explain. Thank you.
In the second example:
Action newAction2 = new Action(() => { System.Console.WriteLine("Inside Action2 " + i); });
the variable i is captured into the lambda. Not the value of i, but the variable i. That means that the value is whatever the loop happens to be at the moment that line of the task actually gets executed, not what it was when the task was created.
One lazy fix is to snapshot the value:
var tmp = i;
Action newAction2 = new Action(() => { System.Console.WriteLine("Inside Action2 " + tmp ); });
Importantly, the scope for "capture" purposes is defined by where the variable is defined. If we define tmp inside the loop, then that is the scope, hence: different tmp (semantically speaking) each loop iteration. However, in a for loop, the scope definition is technically outside the loop, hence why it is shared over all iterations.
You have a typical captured variable scenario:
for (int i = 0; i < 1000; i++) {
...
Action newAction2 = new Action(() => {
System.Console.WriteLine("Inside Action2 " + i); });
Task.Run(newAction2);
All threads use i variable from for loop. However, when threads have started, loop has completed, and thus i == 1000. That's why all the threads return 1000.
This question already has answers here:
The foreach identifier and closures
(7 answers)
Closed 8 years ago.
can someone tell me why the below is not producing the correct results? It is giving me 1233 when I expected 0123.
public static readonly object locker = new object();
public static List<int> queue = new List<int>();
public static void calculate(int input)
{
Thread.Sleep(1000);
lock (locker)
{
queue.Add(input);
}
}
[TestMethod]
public void TestT()
{
int[] _intList = new int[] { 0, 1, 2, 3 };
List<Thread> _threadList = new List<Thread>();
foreach (int num in _intList)
{
Thread t = new Thread(() => calculate(num));
t.Start();
_threadList.Add(t);
}
foreach (Thread t in _threadList) { t.Join(); }
foreach (var t in queue)
{
Console.WriteLine(t);
}
}
When I change it to use a copy of the _intList variable instead, I get the correct results of 0123. Can someone tell me why this is happening? Is it being cached somewhere?
foreach (int num in _intList)
{
int testNum = num;
Thread t = new Thread(() => calculate(testNum));
t.Start();
_threadList.Add(t);
}
When you're passing a variable to a lambda expression it gets captured by the expression. So it's not a copy but that very variable you're getting. This is a common issue with foreach and delayed execution (multithreaded or not), since the foreach continues num is getting it's next value and if it does so because your thread gets to calculate, it's that value that will be calculated.
If you didn't multithread this but instead called the result of the lambda after the foreach what you would see would be 3 3 3 3 instead, in this case you're simply seeing them ofset by one because, most likely, the time it takes to start the thread is about the same as 1 iteration.
When you're making a copy of the variable that variable is declared within the scope of the foreach and it's a new variable each time, it's not getting changed to the next member and that variable is the one getting captured, giving you the correct result. This is the correct way to do this. The result you're getting isn't unexpected but not guaranteed either, you could be getting anything from 0 1 2 3 to 3 3 3 3 with the 1st method, the second method guarantees a correct output.
This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 11 days ago.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyClass myClass = new MyClass();
myClass.StartTasks();
}
}
class MyClass
{
int[] arr;
public void StartTasks()
{
arr = new int[2];
arr[0] = 100;
arr[1] = 101;
for (int i = 0; i < 2; i++)
{
Task.Factory.StartNew(() => WorkerMethod(arr[i])); // IndexOutOfRangeException: i==2!!!
}
}
void WorkerMethod(int i)
{
}
}
}
It seems that i++ gets executed one more time before the loop iteration is finished. Why do I get the IndexOutOfRangeException?
You are closing over loop variable. When it's time for WorkerMethod to get called, i can have the value of two, not the value of 0 or 1.
When you use closures it's important to understand that you are not using the value that the variable has at the moment, you use the variable itself. So if you create lambdas in loop like so:
for(int i = 0; i < 2; i++) {
actions[i] = () => { Console.WriteLine(i) };
}
and later execute the actions, they all will print "2", because that's what the value of i is at the moment.
Introducing a local variable inside the loop will solve your problem:
for (int i = 0; i < 2; i++)
{
int index = i;
Task.Factory.StartNew(() => WorkerMethod(arr[index]));
}
<Resharper plug> That's one more reason to try Resharper - it gives a lot of warnings that help you catch the bugs like this one early. "Closing over a loop variable" is amongst them </Resharper plug>
The reason is that you are using a loop variable inside a parallel task. Because tasks can execute concurrently the value of the loop variable may be different to the value it had when you started the task.
You started the task inside the loop. By the time the task comes to querying the loop variable the loop has ended becuase the variable i is now beyond the stop point.
That is:
i = 2 and the loop exits.
The task uses variable i (which is now 2)
You should use Parallel.For to perform a loop body in parallel. Here is an example of how to use Parallel.For
Alternativly, if you want to maintain you current strucuture, you can make a copy of i into a loop local variable and the loop local copy will maintain its value into the parallel task.
e.g.
for (int i = 0; i < 2; i++)
{
int localIndex = i;
Task.Factory.StartNew(() => WorkerMethod(arr[localIndex]));
}
Using foreach does not throw:
foreach (var i in arr)
{
Task.Factory.StartNew(() => WorkerMethod(i));
}
But is doesn't work either:
101
101
It executes WorkerMethod with the last entry in the array. Why is nicely explained in the other answers.
This does work:
Parallel.ForEach(arr,
item => Task.Factory.StartNew(() => WorkerMethod(item))
);
Note
This actually is my first hands-on experience with System.Threading.Tasks. I found this question, my naive answer and especially some of the other answers useful for my personal learning experience. I'll leave my answer up here because it might be useful for others.