多线程时循环索引超出范围ArgumentOutOfRangeException

3

我遇到了一些奇怪的行为... 当我在ThreadTest方法中迭代dummyTextList时,我会得到一个索引超出范围异常(ArgumentOutOfRangeException),但是如果我删除线程,只打印文本,那么一切都正常。

这是我的主要方法:

public static Object sync = new Object();
static void Main(string[] args)
{
    ThreadTest();
    Console.WriteLine("Press any key to continue.");
    Console.ReadKey();
}

此方法抛出异常:

private static void ThreadTest()
{
    Console.WriteLine("Running ThreadTest");
    Console.WriteLine("Running ThreadTest");
    List<String> dummyText = new List<string>()
    { "One", "Two", "Three", "Four", "Five", 
      "Six", "Seven", "Eight", "Nine", "Ten"};

    for (int i = 0; i < dummyText.Count; i++)
    {
        Thread t = new Thread(() => PrintThreadName(dummyText[i])); // <-- Index out of range?!?
        t.Name = ("Thread " + (i));
        t.IsBackground = true;
        t.Start();
    }
}

private static void PrintThreadName(String text)
{
    Random rand = new Random(DateTime.Now.Millisecond);
    while (true)
    {
        lock (sync)
        {
            Console.WriteLine(Thread.CurrentThread.Name + " running " + text);
            Thread.Sleep(1000+rand.Next(0,2000));
        }
    }
}

这不会抛出异常:
private static void ThreadTest()
{
    Console.WriteLine("Running ThreadTest");
    List<String> dummyText = new List<string>()
    { "One", "Two", "Three", "Four", "Five", 
      "Six", "Seven", "Eight", "Nine", "Ten"};

    for (int i = 0; i < dummyText.Count; i++)
    {
        Console.WriteLine(dummyText[i]); // <-- No exception here
    }
}

有谁知道这是为什么吗?
1个回答

15

当你通过闭包将本地变量传递到线程或ThreadPool委托时,需要复制该变量。例如:

for (int i = 0; i < dummyText.Count; i++)
{
    int index = i;
    Thread t = new Thread(() => PrintThreadName(dummyText[index]));
    // ...
}

如果你不这样做,那么变量基本上是通过引用传递的,在 for 循环的最后一个位置,索引将超出数组的范围(这可能会在闭包执行之前很久就发生)。

2
复制品不应该在lambda表达式之外进行,因为lambda表达式直到线程实际启动才被调用。 - Michael Petito
@Michael:哇,我一定是累了,我从来不会写那样的代码!感谢你快速发现。 - Aaronaught
你真是帮了我大忙了。我为解决这个问题已经奋斗了好几天了,而你的帮助让我节省了很多时间和精力。谢谢! - TheFuzzyGiggler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接