Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple Threading in C# [duplicate]

Possible Duplicate:
C# Captured Variable In Loop

I'm working on a few simple applications of threading, but I can't seem to get this to work:

class ThreadTest
{
    static Queue<Thread> threadQueue = new Queue<Thread>();

    static void Main()
    {
        //Create and enqueue threads
        for (int x = 0; x < 2; x++)
        {
            threadQueue.Enqueue(new Thread(() => WriteNumber(x)));
        }

        while(threadQueue.Count != 0)
        {
            Thread temp = threadQueue.Dequeue();
            temp.Start();
        }

        Console.Read();
    }

    static void WriteNumber(int number)
    {
        for (int i = 0; i < 1000; i++)
        {
            Console.Write(number);
        }
    }
}

The goal basically is to add threads to a queue one by one, and then to go through the queue one by one and pop off a thread and execute it. Because I have "x<2" in my for loop, it should only make two threads - one where it runs WriteNumber(0), and one where it runs WriteNumber(1), which means that I should end up with 1000 0's and 1000 1's on my screen in varying order depending on how the threads are ultimately executed.

What I end up with is 2000 2's. The two possible solutions that I've come up with are: I've missed something glaringly obvious, or sending the variable x to the WriteNumber function is doing a pass-by-reference and not pass-by-value, so that when the threads execute they're using the most recent version of x instead of what it was at the time that the function was set. However, it was my understanding that variables are passed by value by default in C#, and only passed by reference if you include 'ref' in your parameter.

like image 648
Jake Avatar asked Jun 19 '12 21:06

Jake


2 Answers

You're capturing x in the lambda expression. The value of x changes to 2 before you start the threads. You need to make a copy of value within the loop:

for (int x = 0; x < 2; x++)
{
    int copy = x;
    threadQueue.Enqueue(new Thread(() => WriteNumber(copy)));
}

Lambda expressions capture variables. Even though the value passed to WriteNumber is passed by value, it's not called at all until the thread is started - by which time x is 2.

By making a copy within the loop, each iteration of the loop gets its own separate "instance" of the copy variable, and that doesn't change value... so by the time WriteNumber is called, each copy variable still has the same value that x had for that iteration.

like image 107
Jon Skeet Avatar answered Sep 28 '22 11:09

Jon Skeet


This occurs because the value of x is accessed after the loop is finished, and by that time it is 2.

You need to use a temporary variable to prevent variable capturing.

for (int x = 0; x < 2; x++)
{
    int tmp = x;
    threadQueue.Enqueue(new Thread(() => WriteNumber(tmp)));
}
like image 32
Kendall Frey Avatar answered Sep 28 '22 11:09

Kendall Frey