8

all I have searched this question, and I found so many answers to it was not difficult to find a solution for my question. BUT, I have strange experience and I don't know the reason that's why I ask people to give me some advice. Here are my codes:

    void SetThread()
    {
        for (int i = 0; i < _intArrayLength; i++)
        {
            Console.Write(string.Format("SetThread->i: {0}\r\n", i));
            _th[i] = new Thread(new ThreadStart(() => RunThread(i)));
            _th[i].Start();
        }
    }

    void RunThread(int num)
    {
        Console.Write(string.Format("RunThread->num: {0}\r\n", num));
    }

Yes, they are ordinary thread codes. I expect all the thread array should be calling RunThread method 10 times. It should be like

SetThread->i: 0
SetThread->i: 1
SetThread->i: 2
SetThread->i: 3
SetThread->i: 4
SetThread->i: 5
SetThread->i: 6
SetThread->i: 7
SetThread->i: 8
SetThread->i: 9
RunThread->num: 0
RunThread->num: 1
RunThread->num: 2
RunThread->num: 3
RunThread->num: 4
RunThread->num: 5
RunThread->num: 6
RunThread->num: 7
RunThread->num: 8
RunThread->num: 9

This is what I expect to be. The order is not important. But I get the result like below.

SetThread->i: 0
SetThread->i: 1
SetThread->i: 2
The thread '<No Name>' (0x18e4) has exited with code 0 (0x0).
The thread '<No Name>' (0x11ac) has exited with code 0 (0x0).
The thread '<No Name>' (0x1190) has exited with code 0 (0x0).
The thread '<No Name>' (0x1708) has exited with code 0 (0x0).
The thread '<No Name>' (0xc94) has exited with code 0 (0x0).
The thread '<No Name>' (0xdac) has exited with code 0 (0x0).
The thread '<No Name>' (0x12d8) has exited with code 0 (0x0).
The thread '<No Name>' (0x1574) has exited with code 0 (0x0).
The thread '<No Name>' (0x1138) has exited with code 0 (0x0).
The thread '<No Name>' (0xef0) has exited with code 0 (0x0).
SetThread->i: 3
RunThread->num: 3
RunThread->num: 3
RunThread->num: 3
SetThread->i: 4
RunThread->num: 4
SetThread->i: 5
SetThread->i: 6
RunThread->num: 6
RunThread->num: 6
SetThread->i: 7
RunThread->num: 7
SetThread->i: 8
RunThread->num: 8
SetThread->i: 9
RunThread->num: 9
RunThread->num: 10

What I expect is that RunThread function should carry the argument(num) from 0 to 9. And I cannot figure out what that error message is. "The thread '' ~~ and so on. Could anyone give me some clue on this?

5
  • 1
    The error message means nothing, it's just debugging output from Visual Studio I think. Commented Aug 12, 2013 at 0:05
  • 3
    Also, all the threads refer to the loop counter i through a closure. (Created by the lambda you pass to ThreadStart.) They do not get a copy of the current value of i when the Thread object is created, but the value of i when RunThread is finally called, which happens later after you start the thread and it finishes initialising etc - the loop will have progressed at that point. As you see from your output, that's exactly what happens, RunThread prints out the last value seen in SetThread. Commented Aug 12, 2013 at 0:07
  • 1
    Anyway, I think you can prevent this by doing var ii = i; inside the for loop, and using ii in the argument to ThreadStart. That will make the lambdas close over different "instances" of the local variable. (The difference is that the loop counter's scope is outside the loop body, a local variable inside.) Unless I'm confusing things with Javascript. Commented Aug 12, 2013 at 0:14
  • 1
    A common problem, see: blogs.msdn.com/b/ericlippert/archive/2009/11/12/… Commented Aug 12, 2013 at 0:31
  • Interesting, from MSDN: We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The "for" loop will not be changed. Commented Aug 12, 2013 at 1:34

2 Answers 2

6

You are creating a closure over the loop variable - an easy fix is to just create a local copy, so your thread uses the desired value:

void SetThread()
    {
        for (int i = 0; i < _intArrayLength; i++)
        {
           int currentValue = i;
            Console.Write(string.Format("SetThread->i: {0}\r\n", i));
            _th[i] = new Thread(() => RunThread(currentValue));
            _th[i].Start();
        }
    }
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the answer sorry for the late response
3

You may want to change your code like this to use ParameterizedThreadStart delegate:

    for (int i = 0; i < _intArrayLength; i++)
    {
        Console.Write(string.Format("SetThread->i: {0}\r\n", i));
        _th[i] = new Thread((a) => RunThread((int)a));
        _th[i].Start(i);
    }

Otherwise, from your thread entry point delegate () => RunThread(i) you're accessing the variable i from the parent main thread's context, which may change before your new thread may even start.

4 Comments

Parameterized threads are kind of obsolete since the arrival of lambdas.
@usr, I don't think these two things are related. A lambda expression is still used in my code above as a shortcut, but I could rewrite it without lambda yet without parametrizing the thread start, similar to how @BrokenGlass did it: new Thread(delegate() { RunThread(currentValue); })
Imprecise wording on my part. I meant closures in whatever way C# supports them.
@usr, I agree. It's all about how anonymous delegates deal with the variables defined in the same scope. As this excellent series of blog posts call them, the easy kind and the hard kind. One minor advantage of parametrizing a delegate might be that, as long as it doesn't use its parent scope local variables, there is no behind-the-scene creation of an anonymous class by compiler (and a runtime instance of it), which is otherwise involved to support the syntax benefits. I still use it for simple cases like the above :)

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.