Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Captured variables in ParameterizedThreadStart

I have the following code that creates 10 threads which in turn write out messages to the console:

for (int i = 0; i < 10; i++)
{
    {
        Thread thread = new Thread((threadNumber) =>
            {
                for (int j = 0; j < 10; j++)
                {
                    Thread.Sleep(200);
                    Console.WriteLine(string.Format("Thread: {0}, Line: {1}", threadNumber, j));
                }                           
            });
        thread.Start(i);
    }
}

My understanding is that ParameterizedThreadStart takes an object for which a copy of the reference is sent to the thread. If that is the case since I have not made a local copy of i within each loop all new threads would point to the same memory location meaning certain thread numbers could be 'missed out'. Having run this though (and even against a larger number of threads/sleep times) each value of i has its own thread. Can anyone explain why?

like image 998
Maverick Avatar asked Oct 03 '22 13:10

Maverick


1 Answers

You haven't applied anything deferred or "captured" in the sense of creating an anonymous function that would wrap i.

The lambda function here does not reference i anywhere and its state is completely internalized/contained so no issues here:

(threadNumber) =>
{
    for (int j = 0; j < 10; j++)
    {
        Thread.Sleep(200);
        Console.WriteLine(string.Format("Thread: {0}, Line: {1}", threadNumber, j));
    }                           
});

The Start call here:

thread.Start(i);

Passes i by value (i.e. copies its value) because it is a "value type" and it's not captured in any kind of anonymous function. In this sense, it is passed as any normal struct would to any normal method (because this is exactly what is happening).


If instead you had written your lambda as this using i instead of your threadNumber:

{
    for (int j = 0; j < 10; j++)
    {
        Thread.Sleep(200);
        Console.WriteLine(string.Format("Thread: {0}, Line: {1}", i, j));
    }                           
});

Then you would be in trouble. In this case i is referring to the original variable location and will be evaluated whenever the thread executes. This means it could be the current value of i when it was created (unlikely just due to processing times), or the value set later on in the for loop, or the last possible value 10, and quite possibly have the number skip or shared between iterations.

like image 157
Chris Sinclair Avatar answered Oct 07 '22 20:10

Chris Sinclair