问题
When I run the following code piece, a IndexOutOfRangeException is thrown. It appears that i is 2 when the exception is thrown. My understanding is that the new thread is started after the value of i has been changed. Is there a way to make this code safe from this kind of problem?
int x[2] = {1, 3};
int numberOfThreads = 2;
for (int i = 0; i < numberOfThreads; i++)
{
new Thread(() =>
{
DoWork(x[i]);
}).Start();
}
回答1:
The problem is that the variable i
is being captured, and by the time the thread actually gets to start, it's 2.
Use this instead:
for (int i = 0; i < numberOfThreads; i++)
{
int value = x[i];
new Thread(() => DoWork(value)).Start();
}
Or:
foreach (int value in x)
{
int copy = value;
new Thread(() => DoWork(copy)).Start();
}
Or:
for (int i = 0; i < numberOfThreads; i++)
{
int copyOfI = i;
new Thread(() => DoWork(x[copyOfI])).Start();
}
In each case, the lambda expression will capture a new variable on each iteration of the loop - a variable which won't be changed by subsequent iterations.
In general, you should avoid capturing the loop variable in a lambda expression which will be executed later. See Eric Lippert's blog post on the topic for more details.
As of C# 5, it's likely that the foreach
loop behaviour will be changed to avoid this being a problem - but the for
loop equivalent would still be an issue.
回答2:
You are closing over the loop variable, to get the current value of i
use a local copy instead:
for (int i = 0; i < numberOfThreads; i++)
{
int localI = i;
new Thread(() =>
{
DoWork(x[localI]);
}).Start();
}
来源:https://stackoverflow.com/questions/7867906/indexoutofrangeexception-is-thrown-when-starting-a-new-thread