简体   繁体   中英

IndexOutOfRangeException is thrown when starting a new thread

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();
}

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.

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();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM