简体   繁体   中英

Passing a local variable to another thread in .net

In the following code, why we don't get NullReference exception and var2 value is 56 though the TestMethod has certainly finished before 'Messagebox' line? I read this great answer from Eric Lippert and this blog post, but I still don't get it.

void TestMethod()
{
    int var1 = 10;
    List<long> list1 = new List<long>();
    for (int i = 0; i < 5; i++)
        list1.Add(i);

    ThreadPool.QueueUserWorkItem(delegate
    {
        int var2 = var1;
        Thread.Sleep(1000);
        list1.Clear();
        MessageBox.Show(var2.ToString());
    });
    var1 = 56;
}

I think it's because delegate has formed closure around variable var1 . Probably looking at how closure works internally would help you. You can refer to explanation here

The compiler (as opposed to the runtime) creates another class/type. The function with your closure and any variables you closed over/hoisted/captured are re-written throughout your code as members of that class. A closure in .Net is implemented as one instance of this hidden class.

Having that that, I believe roughly compiler generated code would look like :

void TestMethod()
{
    UnspeackableClosureClass closure = new UnspeackableClosureClass(10);
    List<long> list1 = new List<long>();
    for (int i = 0; i < 5; i++)
        list1.Add(i);

    ThreadPool.QueueUserWorkItem(closure.AutoGeneratedMethod);
    closure.closureVar = 56;
}

public class UnspeackableClosureClass
{
   public int closureVar;
   public UnspeackableClosureClass(int val){closureVar=val}

   public void AutoGeneratedMethod(){
     int var2 = closureVar;
     Thread.Sleep(1000);
     list1.Clear();
     MessageBox.Show(var2.ToString());
  }
}

I think what you're saying is that you expect var1 to be deallocated when TestMethod() exits. After all, the local variables are stored on the stack, and when the method exits, the stack pointer has to revert to the spot where it was before the call, meaning that all the local variables are deallocated. If that were really what were happening, var1 might not be set to null at all; it could contain garbage, or bits of some other local variable, created later when the stack pointer moves again. Is that what you mean?

What turned the light on for me is the understanding that asynchronous thinking is not stack-based at all. A stack just doesn't work-- because the order of calls do not form a stack. Instead, bits of code are associated with contextual objects which are held on the heap . They can execute in any order and even simultaneously.

Your delegate needs var1 , so the compiler promotes it from a variable held in the stack to a variable held in one of these objects, associated with the delegate's behavior. This is what is called a "closure" or a "closed variable." To the delegate, it looks just like a local variable, because it is-- just not on the stack any more. It will live as long as that object needs to live, even after TestMethod() has exited.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM