代码之家  ›  专栏  ›  技术社区  ›  Kiril

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

  •  3
  • Kiril  · 技术社区  · 14 年前

    我有一些奇怪的行为…当我迭代 dummyText List ThreadTest 方法I获取超出范围的索引异常( 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 回复  |  直到 14 年前
        1
  •  13
  •   Aaronaught    14 年前

    将局部变量传递到线程或 ThreadPool 通过一个闭包委托,您需要制作一个变量的副本。如:

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

    如果不这样做,那么变量基本上是通过引用传入的,并且索引将在 for 循环(可能在执行关闭之前很久发生)。