Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I specify which variables I want to persist beyond the completion of an await continuation?

Within an async method, any local variables are stored away so that when whatever thread continues after the await will have access to the values. Is there any way to indicate which values are really needed after the await?

For example:

var firstName = "Karl";
var lastName = "Anderson";
var street1 = "123 Nowhere Street";
var street2 = "Apt 1-A";
var city = "Beverly Hills";
var state = "California";
var zip = "90210";

await MyTaskHere();

Console.WriteLine(firstName);
Console.WriteLine(city);

So I have declared 7 local variables, but only use 2 of them after the await, is there any attribute I can decorate my variables with to indicate that I intend to only use firstName and city after the await completes?

Note: This is a contrived example, but it seems like it might be beneficial to be able to curb the storage of potentially large data chunks if they are not needed when the next thread comes to finish the work.

like image 678
Karl Anderson Avatar asked Aug 15 '13 03:08

Karl Anderson


People also ask

Which is the recommended way to wait for an async method to complete?

No problem, just make a loop and call this function with an await: [code] for (int i = pendingList. Count - 1; i >= 0; i--)

Does async await improve performance?

The main benefits of asynchronous programming using async / await include the following: Increase the performance and responsiveness of your application, particularly when you have long-running operations that do not require to block the execution.

Does async await create new thread?

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.

What is difference between async and await in C#?

An async keyword is a method that performs asynchronous tasks such as fetching data from a database, reading a file, etc, they can be marked as “async”. Whereas await keyword making “await” to a statement means suspending the execution of the async method it is residing in until the asynchronous task completes.


2 Answers

No, you cannot. (Other than the obvious solutions of splitting them up into separate methods or setting them to null).

The compiler is not fully optimized in this scenario; it may capture more variables than it needs and may hold onto them longer than necessary. This is probably something Microsoft will optimize in the future.

like image 151
Stephen Cleary Avatar answered Nov 12 '22 22:11

Stephen Cleary


You can run Ildasm.exe on your program to see what code the compiler generates. I've tried to do this but unfortunately my IL skills are a bit lacking, however you can see that all the local variables are captured as fields of the generated <Foo>d__0 class. Given this program:

using System;
using System.Threading.Tasks;

namespace AsyncCaptureVariables
{
    class Program
    {
        public async Task Foo()
        {
            var firstName = "Karl";
            var lastName = "Anderson";
            var street1 = "123 Nowhere Street";
            var street2 = "Apt 1-A";
            var city = "Beverly Hills";
            var state = "California";
            var zip = "90210";

            await Task.Delay(5000);

            Console.WriteLine(firstName);
            Console.WriteLine(city);
        }

        public static void Main()
        {
            var program = new Program();
            Task t = program.Foo();
            t.Wait();
        }
    }
}

The compiler generates something like the following partially converted to C# code:

using System;

class Program : System.Object
{
class <Foo>d__0 : System.ValueType, System.Runtime.CompilerServices.IAsyncStateMachine
  {   
    public int32 <>1__state;
    public System.Runtime.CompilerServices.AsyncTaskMethodBuilder <>t__builder;
    public class AsyncCaptureVariables.Program <>4__this;
    public string <firstName>5__1;
    public string <lastName>5__2;
    public string <street1>5__3;
    public string <street2>5__4;
    public string <city>5__5;
    public string <state>5__6;
    public string <zip>5__7;
    private System.Runtime.CompilerServices.TaskAwaiter <>u__$awaiter8;
    private object <>t__stack;

    void  MoveNext()
    {      
      try
      {      
        IL_0000:  ldc.i4.1
                IL_0001:  stloc.0
                IL_0002:  ldarg.0
                IL_0003:  ldfld      int32 AsyncCaptureVariables.Program/'<Foo>d__0'::'<>1__state'
                IL_0008:  stloc.2
                IL_0009:  ldloc.2
                IL_000a:  ldc.i4.s   -3
                IL_000c:  beq.s      IL_0014

                IL_000e:  ldloc.2
                IL_000f:  ldc.i4.0
                IL_0010:  beq.s      IL_0019

                IL_0012:  br.s       IL_001e

                IL_0014:  br         IL_00ee

                IL_0019:  br         IL_00a8

                IL_001e:  br.s       IL_0020

        //000009:         {
                IL_0020:  nop
        //000010:             var firstName = "Karl";
                IL_0021:  ldarg.0
                IL_0022:  ldstr      "Karl"
                IL_0027:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<firstName>5__1'
        //000011:             var lastName = "Anderson";
                IL_002c:  ldarg.0
                IL_002d:  ldstr      "Anderson"
                IL_0032:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<lastName>5__2'
        //000012:             var street1 = "123 Nowhere Street";
                IL_0037:  ldarg.0
                IL_0038:  ldstr      "123 Nowhere Street"
                IL_003d:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<street1>5__3'
        //000013:             var street2 = "Apt 1-A";
                IL_0042:  ldarg.0
                IL_0043:  ldstr      "Apt 1-A"
                IL_0048:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<street2>5__4'
        //000014:             var city = "Beverly Hills";
                IL_004d:  ldarg.0
                IL_004e:  ldstr      "Beverly Hills"
                IL_0053:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<city>5__5'
        //000015:             var state = "California";
                IL_0058:  ldarg.0
                IL_0059:  ldstr      "California"
                IL_005e:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<state>5__6'
        //000016:             var zip = "90210";
                IL_0063:  ldarg.0
                IL_0064:  ldstr      "90210"
                IL_0069:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<zip>5__7'
        //000017: 
        //000018:             await Task.Delay(5000);
                IL_006e:  ldc.i4     0x1388
                IL_0073:  call       class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Threading.Tasks.Task::Delay(int32)
                IL_0078:  callvirt   instance valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter [mscorlib]System.Threading.Tasks.Task::GetAwaiter()
                IL_007d:  stloc.3
        IL_0078:  callvirt   instance valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter [mscorlib]System.Threading.Tasks.Task::GetAwaiter()
        IL_007d:  stloc.3
        IL_007e:  ldloca.s   CS$0$0001
        IL_0080:  call       instance bool [mscorlib]System.Runtime.CompilerServices.TaskAwaiter::get_IsCompleted()
        IL_0085:  brtrue.s   IL_00c6

        IL_0087:  ldarg.0
        IL_0088:  ldc.i4.0
        IL_0089:  stfld      int32 AsyncCaptureVariables.Program/<Foo>d__0::<>1__state
        IL_008e:  ldarg.0
        IL_008f:  ldloc.3
        IL_0090:  stfld      valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter AsyncCaptureVariables.Program/<Foo>d__0::<>u__$awaiter8
        IL_0095:  ldarg.0
        IL_0096:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder AsyncCaptureVariables.Program/<Foo>d__0::<>t__builder
        IL_009b:  ldloca.s   CS$0$0001
        IL_009d:  ldarg.0
        IL_009e:  call       instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted<valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter,valuetype AsyncCaptureVariables.Program/<Foo>d__0>(!!0&,
                                                                                                                                                                                                                                                         !!1&)
        IL_00a3:  nop
        IL_00a4:  ldc.i4.0
        IL_00a5:  stloc.0
        IL_00a6:  leave.s    IL_011d

        IL_00a8:  ldarg.0
        IL_00a9:  ldfld      valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter AsyncCaptureVariables.Program/<Foo>d__0::<>u__$awaiter8
        IL_00ae:  stloc.3
        IL_00af:  ldarg.0
        IL_00b0:  ldloca.s   CS$0$0002
        IL_00b2:  initobj    [mscorlib]System.Runtime.CompilerServices.TaskAwaiter
        IL_00b8:  ldloc.s    CS$0$0002
        IL_00ba:  stfld      valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter AsyncCaptureVariables.Program/<Foo>d__0::<>u__$awaiter8
        IL_00bf:  ldarg.0
        IL_00c0:  ldc.i4.m1
        IL_00c1:  stfld      int32 AsyncCaptureVariables.Program/<Foo>d__0::<>1__state
        IL_00c6:  ldloca.s   CS$0$0001
        IL_00c8:  call       instance void [mscorlib]System.Runtime.CompilerServices.TaskAwaiter::GetResult()
        IL_00cd:  nop
        IL_00ce:  ldloca.s   CS$0$0001
        IL_00d0:  initobj    [mscorlib]System.Runtime.CompilerServices.TaskAwaiter
//000019: 
//000020:             Console.WriteLine(firstName);
        IL_00d6:  ldarg.0
        IL_00d7:  ldfld      string AsyncCaptureVariables.Program/<Foo>d__0::<firstName>5__1
        IL_00dc:  call       void [mscorlib]System.Console::WriteLine(string)
        IL_00e1:  nop
//000021:             Console.WriteLine(city);
        IL_00e2:  ldarg.0
        IL_00e3:  ldfld      string AsyncCaptureVariables.Program/<Foo>d__0::<city>5__5
        IL_00e8:  call       void [mscorlib]System.Console::WriteLine(string)
        IL_00ed:  nop
//000022:         }
//000023: 
//000024:         public static void Main()
//000025:         {
//000026:             var program = new Program();
//000027:             Task t = program.Foo();
//000028:             t.Wait();
//000029:         }
//000030:     }
//000031: }
        IL_00ee:  leave.s    IL_0108

      }  // end .try
      catch [mscorlib]System.Exception 
      {
        IL_00f0:  stloc.1
        IL_00f1:  ldarg.0
        IL_00f2:  ldc.i4.s   -2
        IL_00f4:  stfld      int32 AsyncCaptureVariables.Program/<Foo>d__0::<>1__state
        IL_00f9:  ldarg.0
        IL_00fa:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder AsyncCaptureVariables.Program/<Foo>d__0::<>t__builder
        IL_00ff:  ldloc.1
        IL_0100:  call       instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::SetException(class [mscorlib]System.Exception)
        IL_0105:  nop
        IL_0106:  leave.s    IL_011d

      }  // end handler
      IL_0108:  nop
//000022:         }
      IL_0109:  ldarg.0
      IL_010a:  ldc.i4.s   -2
      IL_010c:  stfld      int32 AsyncCaptureVariables.Program/<Foo>d__0::<>1__state
//000023: 
//000024:         public static void Main()
//000025:         {
//000026:             var program = new Program();
//000027:             Task t = program.Foo();
//000028:             t.Wait();
//000029:         }
//000030:     }
//000031: }
      IL_0111:  ldarg.0
      IL_0112:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder AsyncCaptureVariables.Program/<Foo>d__0::<>t__builder
      IL_0117:  call       instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::SetResult()
      IL_011c:  nop
      IL_011d:  nop
      IL_011e:  ret
    } // end of method <Foo>d__0::MoveNext

    .method private hidebysig newslot virtual final 
            instance void  SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine param0) cil managed
    {
      .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
      .override [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine
      // Code size       13 (0xd)
      .maxstack  8
      IL_0000:  ldarg.0
      IL_0001:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder AsyncCaptureVariables.Program/<Foo>d__0::<>t__builder
      IL_0006:  ldarg.1
      IL_0007:  call       instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine)
      IL_000c:  ret
    } // end of method <Foo>d__0::SetStateMachine

  } // end of class <Foo>d__0
like image 42
NeddySpaghetti Avatar answered Nov 12 '22 23:11

NeddySpaghetti