Confusing thread behaviour [duplicate] - c#

This question already has answers here:
creating new threads in a loop
(2 answers)
Closed 9 years ago.
In C#, if I execute
for (int i = 0;i < 10;i++)
new Thread(() => Console.Write(i)).Start();
I will possibly get 0223557799, that's strange, since i is a int, I think it should be copied before the thread starts.

Closures are your problem here.
Basically, instead of grabbing the value when you create the lambda (in the loop), it grabs it when it needs it. And computers are so fast that by the time that happens, it's already changed. It can't go through the whole loop, but it goes through some of it.
Here's a fix:
for (int i = 0; i < 10; i++)
{
var n = i;
new Thread(() => Console.Write(n)).Start();
}

Because Start() returns immediately, i++ happens before the thread gets a chance to print i to the console. I believe that a workaround is to create a local copy of the int, then print that:
for (int i = 0;i < 10;i++) {
int j = i;
new Thread(() => Console.Write(j)).Start();
}

What basically is happening is this:
You want to start a thread that prints the value of i.
The thread starts.
The code operating in the thread gets the value if i. Note that the value of i can be changed by now.
The value of i gets printed. But no guarantees to get a logical output.
Copy the value of i into another variable first and then print that value. The other answers provide enough samplecode.

Your lambda will be translated into set of the method and context class which will handle a refference to the i.

I would use the built in .NET parallelism support Task Parallelism
You won't have to worry about managing the threads it's done for you.
Example your code converted to the Parallelism libraries.
Parallel.For(0, 10, (i, state) =>
{
Console.WriteLine(i);
});

Related

C# for loop with Task array IndexOutOfRangeException [duplicate]

This question already has answers here:
IndexOutOfRangeException exception when using tasks in for loop in C#
(1 answer)
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 3 years ago.
im starting 10 tasks to get result from web api. Im get IndexOutOfRangeException on offsets array. But how its posible.
Actually 'i' variable canot be greater or equal that 10.
Can anyone help with this?
For loop not working corectly?
times = 10;
Task[] tasks = new Task[times];
int[] offsets = new int[times];
for (int i = 0; i < times; i++)
{
offsets[i] = offset;
tasks[i] = Task.Run(() => SearchLocalByQuery(query, offsets[i], (i + 1)));
offset += Limit;
}
Task.WaitAll(tasks);
i = 10,
i cannot be 10 in for loop off this example.
System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
i cannot be 10 in for loop off this example.
It can be in the lambda expression. This expression:
() => SearchLocalByQuery(query, offsets[i], (i + 1))
captures the variable i, and will evaluate that variable value whenever it is executed. Task.Run returns immediately but won't necessarily have started executing the delegate yet - so it's entirely possible that the loop has moved onto the next iteration, or completed, before i is evaluated.
The simple fix for this is to declare a local variable to capture i inside the loop:
for (int i = 0; i < times; i++)
{
int copy = i;
offsets[i] = offset;
// Make sure we only use copy within the lambda expression
tasks[i] = Task.Run(() => SearchLocalByQuery(query, offsets[copy], copy + 1));
offset += Limit;
}
Now there'll be a fresh copy variable for each iteration of the loop, and that variable will never change its value - so the lambda expression will execute using "the value of i for that iteration of the loop" regardless of what the current value of i is.

ArgumentOutOfRangeException. But it should not be there

So I have and method like this.
var someColletion = _someService.GetSomeCollection(someParam);
var taskCollection = new Task<double>[someCollection.Count];
for (int i = 0; i < taskCollection.Length; i++)
{
// do some stuff on the i-th element of someCollection and taskCollection
// and start the i-th task
}
Task.WaitAll(taskCollection);
double total = 0;
for (int i = 0; i < taskCollection.Length; i++)
{
// get the result of each task and sum it in total variable
}
return total;
the case is when it comes into first for loop and the number of elements in both collections are suppose 1 the ArgumentOutOfRangeException is being thrown and then AggregateException is being thrown on Task.WaitAll() because the i becomes 1 (I don't know why but it does) and when it tries to access the i-th (second) element in array that contains just one element, this happens. But there is more to this. If i set a break point before first loop and go step by step then this thing does not happen. when i becomes one the cycle ends. and everything's okay. now the method I provided above is called by an ASP.NET MVC Controller's Action which itself is called Asynchronously (by ajax call) suppose 3 times. and out of this three just one executes correctly other two do the thing I said above (if not breakpointed). I think that this problem is caused by ajax call most probably because when I breakpoint it stops other calls from executing. Can anyone suggest anything ?
I suspect you're using i within the first loop, capturing it with a lambda expression or anonymous method, like this:
for (int i = 0; i < taskCollection.Length; i++)
{
taskCollection[i] = Task.Run(() => Console.WriteLine(i));
}
If that's the case, it's the variable i which is being captured - not the value of the variable for that iteration of the loop. So by the time the task actually executes, it's likely that the value of i has changed. The solution is to take a copy of the iteration variable within the loop, in a separate "new" variable, and capture that in the anonymous function instead:
for (int i = 0; i < taskCollection.Length; i++)
{
int copy = i;
taskCollection[i] = Task.Run(() => Console.WriteLine(copy));
}
That way, each task captures a separate variable, whose value never changes.

Passing data to a thread using Lambda expressions

for (int i = 0; i < 10; i++)
new Thread (() => Console.Write (i)).Start();
As expected the output of the above code is non-deterministic, because i variable refers to the same memory location throughout the loop’s lifetime. Therefore, each thread calls Console.Write on a variable whose value may change as it is running
However,
for (int i = 0; i < 10; i++)
{
int temp = i;
new Thread (() => Console.Write (temp)).Start();
}
Is also giving non-deterministic output! I thought variable temp was local to each loop iteration. Therefore, each thread captured a different memory location and there should have been np problem.
Your program should have 10 lambdas, each writing one of the digits from 0 to 9 to the console. However, there's no guarantee that the threads will execute in order.
Is also giving non-deterministic output!
No, its not. I have checked ten times your first code (had repeating numbers) and the second (had not).
So it all works fine. Just as it should.
The second code snippet should be deterministic in the sense that each thread eventually writes its temp, and all their temps will differ.
However, it does not guarantee that threads will be scheduled for execution in the order of their creation. You'll see all possible temps, but not necessarily in ascending order.
Here is the proof that OP is right and both pieces of his code are incorrect.
And there is also a solution with proof also.
However need to note that 'non-deterministic' means that the threads receive wrong parameter. The order will be never guaranteed.
The code below examines second piece of OP code and demonstrates that it is working as expected..
I am storing the pair (thread identity, parameter) and then print it to compare with the thread output to prove the pairs aren't changed. I also added few hundreds millisecond random sleep so the for index should obviously change at those times.
Dictionary<int, int> hash = new Dictionary<int, int>();
Random r = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < 10; i++)
{
int temp = i;
var th = new Thread(() =>
{
Thread.Sleep(r.Next(9) * 100);
Console.WriteLine("{0} {1}",
Thread.CurrentThread.GetHashCode(), temp);
});
hash.Add(th.GetHashCode(), temp);
th.Start();
}
Thread.Sleep(1000);
Console.WriteLine();
foreach (var kvp in hash)
Console.WriteLine("{0} {1}", kvp.Key, kvp.Value);

.NET 4.5 parallel processing and for loop

I am trying to create a list of tasks which depend on the number of processors available. I have a for loop which which seems to be behaving strangely. I am aware of the concept of closures in javascript, and it seems like something similar could be happening here:
var tasks = new Task[Environment.ProcessorCount];
for(int x = 0; x < Environment.ProcessorCount; x ++)
{
tasks[x] = Task.Run(() => new Segment(SizeOfSegment, x * SizeOfSegment, listOfNumbers).generateNewList());
}
What I am finding is when I break on the line in the for loop, the variable x seems to be correct, so it starts at 0 and ends at 3 (number of processors is 4). But when I put the break point within the constructor for Segment, I am finding that x was actually 4 when stepping back in the Call Stack.
Any help would be greatly appreciated.
You're capturing x within your lambda expression - but you've got a single x variable which changes values across the course of the loop, so by the time your task actually runs, it may well have a different value. You need to create a copy of the variable inside the loop, creating a new "variable instance" on each iteration. Then you can capture that variable safely:
for(int x = 0; x < Environment.ProcessorCount; x ++)
{
int copy = x;
tasks[x] = Task.Run(() => new Segment(SizeOfSegment,
copy * SizeOfSegment,
listOfNumbers).generateNewList());
}
(I'd also advise you to rename generateNewList to GenerateNewList to comply with .NET naming conventions.)

Why c# doesn't preserve the context for an anonymous delegate calls?

I have the following method:
static Random rr = new Random();
static void DoAction(Action a)
{
ThreadPool.QueueUserWorkItem(par =>
{
Thread.Sleep(rr.Next(200));
a.Invoke();
});
}
now I call this in a for loop like this:
for (int i = 0; i < 10; i++)
{
var x = i;
DoAction(() =>
{
Console.WriteLine(i); // scenario 1
//Console.WriteLine(x); // scenario 2
});
}
in scenario 1 the output is: 10 10 10 10 ... 10
in scenario 2 the output is: 2 6 5 8 4 ... 0 (random permutation of 0 to 9)
How do you explain this? Is c# not supposed to preserve variables (here i) for the anonymous delegate call?
The problem here is that there is one i variable and ten instances / copies of x. Each lambda gets a reference to the single variable i and one of the instances of x. Every x is only written to once and hence each lambda sees the one value which was written to the value it references.
The variable i is written to until it reaches 10. None of the lambdas run until the loop completes so they all see the final value of i which is 10
I find this example is a bit clearer if you rewrite it as follows
int i = 0; // Single i for every iteration of the loop
while (i < 10) {
int x = i; // New x for every iteration of the loop
DoAction(() => {
Console.WriteLine(i);
Console.WriteLine(x);
});
i++;
};
DoAction spawns the thread, and returns right away. By the time the thread awakens from its random sleep, the loop will be finished, and the value of i will have advanced all the way to 10. The value of x, on the other hand, is captured and frozen before the call, so you will get all values from 0 to 9 in a random order, depending on how long each thread gets to sleep based on your random number generator.
I think you'll get the same result with java or any Object oriented Language (not sure but here it seems logical).
The scope of i is for the whole loop and the scope of x is for each occurrence.
Resharper helps you top spot this kind of problem.

Categories