-1

Here is the simple code,

class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 100; i++)
        {
            Thread thread = new Thread(new ThreadStart(()=> SimpleClass.Instance.weird.SetHello(i)));
            thread.Start();
        }

        Console.Read();
    }
}

interface IClass
{
    WeirdClass weird{ get; set; }
}

class SimpleClass : IClass {
    public static SimpleClass Instance = new SimpleClass();

    public WeirdClass weird{ get; set; }

    public SimpleClass()
    {
        weird= new WeirdClass();
    }
}

class WeirdClass
{
    public int hello;

    public void SetHello(int i)
    {
        this.hello = i;
        Console.WriteLine(this.hello);
    }
}

We could see the 'hello' value in WeirdClass is not correct in multi-thread, the value is just like a static instance, but it is not.

Maybe the magic happens on SimpleClass.Instance.async, so could anyone give me some explanation about that ? Thanks

liuzhidong
  • 538
  • 3
  • 18
  • 1
    Please read [ask] and be explicit. How is it "corrupted"? You do realize that you're working with one `WeirdClass` instance? – CodeCaster May 19 '16 at 10:38
  • 2
    I'd also *strongly* advise against the use of `async` as an identifier, given that it's a contextual keyword... – Jon Skeet May 19 '16 at 10:43
  • 2
    Anyway you're accessing a closure, see [Access to Modified Closure (2)](http://stackoverflow.com/questions/304258/access-to-modified-closure-2). – CodeCaster May 19 '16 at 10:47

1 Answers1

0

I upvoted your question since it brought me to some interesting research.

In the for loop assign the i variable to a temp ii and use ii in the thread initialization, like this:

var ii = i;
Thread thread = new Thread(
    new ThreadStart(()=> SimpleClass.Instance.async.SetHello(ii)));
thread.Start();

I've tested it with different configurations and different contexts (for example, put a random Thread.Sleep between hello assignment and Console.WriteLine).

What my solution gets is this: every thread has its own unique i and gets it written on the Console a single time. But the numbers are not written in order, since the threads don't start sequentially as you create them.

Just a side-note: as @JonSkeet recommended, don't use async as identifier, it's a reserved word. Use instead AsyncWeird, InnerClass, HelloContainer, or whatever that is not an official C# token, otherwise your code becomes less clear.

UPDATE - SOLUTION:

This:

for(int i = 0; i < 10; i++)

is equivalent to this:

int i;
for(i = 0; i < 10; i++)

That is, in both cases i is created outside the for loop, and so it is somehow shared between different cycles of the loop. If you check the link @CodeCaster provided, you'll see the same issue with the foreach loop with previous versions of C#.

If you declare the thread in a cycle with i as input, you're telling to the thread: "ok, when you start, get the i value!". In that moment, i has the proper value, but the thread asks for i only when it's effectively started, so the value has already changed because in the meantime the main thread has already begun a new cycle of the loop and has already re-assigned the i for the next iteration.

Instead, if you create a temp variable ii inside the loop, it remains in that cycle and cannot be seen outside.

This is a demonstration of the whole game: try to declare ii outside of the loop... the behavior is again the incorrect one, that you showed with the i variable.

animuson
  • 53,861
  • 28
  • 137
  • 147
  • yes, your code gives me the correct result.... what's the difference... – liuzhidong May 19 '16 at 14:18
  • You'll have to read [this blog post](https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/) if you want to post a useful and correct answer. – Hans Passant May 19 '16 at 16:41
  • @MK87: This behavior changed with new versions of the C# compiler. Note that it's determined by the .NET compiler version used to build the project, it's not a runtime compatibility issue. – Ben Voigt May 20 '16 at 02:19