1

I am new to C# and multithreading concept and was exploring multithreading. I am getting unexpected output. Following is the program I wrote in c#:-

static void Main(string[] args)
{

    for (int i = 1; i <= 5; i++)
    {

        System.Threading.Thread workerThread = new System.Threading.Thread(() => fn(i));
        workerThread.Start();

    }

    System.Console.WriteLine("!End the main process!");

}

public static void fn(int x)
{
    System.Console.WriteLine($"fn method called for {x}");
    System.Threading.Thread.Sleep(5000);
    for (int i = 1; i <=2 ; i++)
    {
        System.Console.Write($" {x} ");
    }
}

Expected Output:- One of the expected output would have been:-

  • !End the main process!
  • fn method called for 1
  • fn method called for 2
  • fn method called for 3
  • fn method called for 4
  • fn method called for 5
  • 1 1 2 2 3 3 4 4 5 5

Actual Output what I am getting is:-

  • !End the main process!
  • fn method called for 4
  • fn method called for 3
  • fn method called for 2
  • fn method called for 5
  • fn method called for 2
  • 2 2 2 4 2 4 3 3 5 5

Why the function fn was called twice of 2 and why it wasn't called for 1?

and when I re-run the function I am getting

  • !End the main process!
  • fn method called for 3
  • fn method called for 4
  • fn method called for 3
  • fn method called for 6
  • fn method called for 4
  • 3 3 3 3 4 4 6 4 6 4

How did fn end up getting called for 6?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Vansh
  • 43
  • 4
  • Try `Thread workerThread = new Thread(fn); workerThread.Start(i);`, with `void fn(object x)` -- It's the lambda + closure you see at work. See, e.g., [Captured variable in a loop in C#](https://stackoverflow.com/q/271440/7444103) -- [C# Closures, why is the loopvariable captured by reference?](https://stackoverflow.com/q/1930133/7444103) -- [What is the exact definition of a closure?](https://stackoverflow.com/q/1095707/7444103) – Jimi Nov 06 '22 at 06:53

1 Answers1

1

Because you are passing i as an argument, it depends on what the value of i is at the specific moment the method is executed. If the method doesn't get executed until your loop has moved on to the next iteration, it will get the incremented value of i.

The solution is to not use a variable that has that outer scope. Declare a local variable specifically for the method argument:

for (int i = 1; i <= 5; i++)
{
    var arg = i;

    System.Threading.Thread workerThread = new System.Threading.Thread(() => fn(arg));
    workerThread.Start();    
}

arg is allocated anew for each iteration so every method call will get the correct value of arg.

jmcilhinney
  • 50,448
  • 5
  • 26
  • 46
  • If you use common terms like captured variables, this answer can be easier to understand. Articles like https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions#capture-of-outer-variables-and-variable-scope-in-lambda-expressions and https://unicorn-dev.medium.com/how-to-capture-a-variable-in-c-and-not-to-shoot-yourself-in-the-foot-d169aa161aa6 can be the references. – Lex Li Nov 06 '22 at 06:39