3

I'm getting some strange behavior... when I iterate over the dummyText List in the ThreadTest method I get an index out of range exception (ArgumentOutOfRangeException), but if I remove the threads and I just print out the text, then everything works fine.

This is my main method:

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

This method throws the exception:

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

This does not throw the exception:

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
    }
}

Does anybody know why this is happening?

0

1 Answer 1

15

When you pass a local variable into a thread or ThreadPool delegate through a closure, you need to make a copy of the variable. As in:

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

If you don't do this, then the variable basically gets passed in by reference, and the index will exceed the bounds of the array at the very end of the for loop (which may happen long before the closure is ever executed).

Sign up to request clarification or add additional context in comments.

3 Comments

Shouldn't the copy be made outside of the lambda expression, since the lambda expression isn't invoked until the thread actually starts?
@Michael: Wow, I must be getting tired, I would never write that! Thanks for the quick catch.
You just saved me SO much time and headache. I've been fighting with this for days narrowing down this bug. +1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.