Consider the below block of code:
int noThreads = noProcessors - 1;
var countdownEvent = new CountdownEvent(noThreads);
if (noThreads == 0)
noThreads = 1;
float blockSize = (float)jobList.Count / noThreads;
int startIndex, stopIndex;
for (int i = 0; i < noThreads; i++)
{
startIndex = i * (int)blockSize;
stopIndex = (i + 1) * (int)blockSize;
if (i == noThreads - 1)
stopIndex = jobList.Count;
new Thread(delegate()
{
for (int j = startIndex; j < stopIndex; j++)
{
CreateJobAndAddToCollection(jobs[j], jobList, j);
}
countdownEvent.Signal();
}).Start();
}
countdownEvent.Wait();
I want to know whether the variables startIndex and stopIndex within the delegate point to the same location as their namesakes outside the delegate. From my testing I think they do, which surprises me as they are value types, and I would have thought that the code inside the delegate would be considered a separate method. Apologies if this has been asked before, I haven't been able to find any other information about it.
I want to know whether the variables startIndex and stopIndex within the delegate point to the same location as their namesakes outside the delegate.
Yes, they do. The variables themselves are captured - changes made within the delegate will be visible outside the delegate, and vice versa.
However, this is only a problem because you've declared startIndex
and stopIndex
outside the loop. If you declare them inside the loop, each iteration will use a separate pair of variables.
for (int i = 0; i < noThreads; i++)
{
int startIndex = i * (int)blockSize;
int stopIndex = (i + 1) * (int)blockSize;
// Code as before
}
Each delegate will now capture a separate startIndex
and stopIndex
pair, and the rest of the code in the loop doesn't modify those variables, so you'll be "safe". In other words, if your loop code contained:
startIndex++; stopIndex++;
at the end, then the delegate would still see those changes (assuming the new thread actually executed the delegate after the end of the iteration...) But the new pair of variables on the next iteration of the loop will be entirely independent from this iteration.
Avoiding threads, here's a simple example to show all this:
using System;
using System.Collections.Generic;
class Test
{
static void Main()
{
int outerCounter = 0;
var actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
outerCounter = i * 10;
int innerCounter = i * 10;
// Using an anonymous method here as per question;
// you'd see the same behaviour with a lambda expression.
actions.Add(delegate {
Console.WriteLine("{0}: {1}", outerCounter, innerCounter);
});
innerCounter++;
}
foreach (var action in actions)
{
action();
}
}
}
The output is:
40: 1
40: 11
40: 21
40: 31
40: 41
This shows:
outerCounter
variable, which has a value of 40 at the end of the for
loop.innerCounter
variable, which has a value of i * 10
when the delegate is created, but i * 10 + 1
by the time the delegate is executed due to the innerCounter++
line.See more on this question at Stackoverflow