4

I am trying to understand why this program doesn't work

Expected output: numbers 0-19 in random order What I get when I run: some numbers repeat, sometimes 20 is printed.

Please help. I tried with lock(obj) in DoSomething() but it didn't help.

Program

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication2
{
    public delegate void callbackDelegate(int x);
    class Program
    {
        void processCallback(int x)
        {
            Console.WriteLine("IN callback: The value I got is " + x);
        }

        static void Main(string[] args)
        {
            Program p = new Program();
            p.processinThreads();
            Console.ReadKey();
        }

        public void DoSomething(int x, callbackDelegate callback)
        {
            Thread.Sleep(1000);
            //Console.WriteLine("I just woke up now " + x);
            callback(x);
        }

        public void processinThreads()
        {
            for (int i = 0; i < 20; i++)
            {
                Thread t = 
new Thread(new ThreadStart(()=>DoSomething(i, processCallback)));
                t.Start();
            }
        }
    }
}
3
  • possible duplicate of C# Captured Variable In Loop Commented Feb 23, 2012 at 12:03
  • right, I couldn't get this when I searched for this problem. Well I don't know this 'closure over lambda problem' :) Commented Feb 23, 2012 at 12:08
  • Avoid manual thread creation. Here is a detailed explanation with benchmarks why Commented Feb 23, 2012 at 13:49

3 Answers 3

9
public void processinThreads()
{
    for (int i = 0; i < 20; i++)
    {
        int local = i;
        Thread t = new Thread(new ThreadStart(()=>DoSomething(local, processCallback)));
        t.Start();
    }
}

Your problem is related to closure over lambda.

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

1 Comment

Check out this article: Closing over the loop variable considered harmful. It will be fixed in C# 5.0.
7

You should just use the TPL, its a lot easier and recommended over manual thread management:

Parallel.For(0, 20, x => {
    Thread.Sleep(1000);
    Console.WriteLine("IN callback: The value I got is " + x);
});

This will also block until the loop finishes, if you don't want that you can use the TPL Task, but I would definitely recommend avoiding threads.

2 Comments

Moreover, this is likely to be more efficient: Spawning another thread is pretty expensive. The TPL uses an (automagically computed) optimum number of threads and re-uses them.
@user676571 I am planning to use TPL with Async pattern applied.
1

As Jakub already told you, you need to copy i into another local variable local. In your code, the Delegates have direct access to i itself, not a copy of i, so they print out the very current value of i, which may be greater than when you started the thread. This is called closure.

Comments

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.