1

当我运行以下代码时,会引发 IndexOutOfRangeException。抛出异常时 i 似乎是 2。我的理解是,新线程是在 i 的值更改后启动的。有没有办法让这段代码免受这类问题的影响?

int x[2] = {1, 3};
int numberOfThreads = 2;

for (int i = 0; i < numberOfThreads; i++)
{
    new Thread(() =>
    {
        DoWork(x[i]);
    }).Start();
}
4

2 回答 2

6

问题是变量 i正在被捕获,当线程实际开始时,它是 2。

改用这个:

for (int i = 0; i < numberOfThreads; i++)
{
    int value = x[i];
    new Thread(() => DoWork(value)).Start();
}

或者:

foreach (int value in x)
{
    int copy = value;
    new Thread(() => DoWork(copy)).Start();
}

或者:

for (int i = 0; i < numberOfThreads; i++)
{
    int copyOfI = i;
    new Thread(() => DoWork(x[copyOfI])).Start();
}

在每种情况下,lambda 表达式都会在循环的每次迭代中捕获一个新变量——一个不会被后续迭代更改的变量。

通常,您应该避免在稍后执行的 lambda 表达式中捕获循环变量。有关更多详细信息,请参阅Eric Lippert关于该主题的博客文章。

从 C# 5 开始,很可能foreach会更改循环行为以避免这成为问题 - 但for循环等效项仍然是一个问题。

于 2011-10-23T17:41:21.143 回答
2

您正在关闭循环变量,以获取使用本地副本的当前值:i

for (int i = 0; i < numberOfThreads; i++)
{
    int localI = i;
    new Thread(() =>
    {
        DoWork(x[localI]);
    }).Start();
}
于 2011-10-23T17:41:42.113 回答