在启动新线程时抛出IndexOutOfRangeException

本文关键字:IndexOutOfRangeException 线程 启动 新线程 | 更新日期: 2023-09-27 18:12:02

当我运行下面的代码段时,抛出一个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();
}

在启动新线程时抛出IndexOutOfRangeException

问题是变量 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循环的等效仍然是一个问题。

您正在关闭循环变量,以获得当前值 i使用本地复制代替:

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