我可以指定在等待延续完成之后要保留哪些变量吗

本文关键字:保留 变量 之后 在等待 延续 我可以 | 更新日期: 2023-09-27 18:27:49

async方法中,任何局部变量都会存储起来,以便在await之后继续执行任何线程时都可以访问这些值。有没有什么方法可以指示await之后真正需要哪些值?

例如:

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);

所以我已经声明了7个局部变量,但在await之后只使用其中的2个,有没有任何属性可以用来修饰我的变量,以表明我打算在await完成后只使用firstNamecity

注意:这是一个人为的例子,但如果在下一个线程完成工作时不需要潜在的大数据块,那么能够控制这些数据块的存储似乎是有益的。

我可以指定在等待延续完成之后要保留哪些变量吗

不,不能。(除了将它们拆分为单独的方法或将它们设置为null的明显解决方案之外)。

在这种情况下,编译器没有完全优化;它可能会捕获比它需要的更多的变量,并且可能会保留它们比必要的时间更长。这可能是微软未来会优化的东西。

您可以在程序上运行Ildasm.exe来查看编译器生成的代码。我尝试过这样做,但不幸的是,我的IL技能有点欠缺,但您可以看到,所有的局部变量都被捕获为生成的<Foo>d__0类的字段。给定此程序:

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();
        }
    }
}

编译器生成如下部分转换为C#代码的内容:

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

您可以通过在代码中添加大括号来创建作用域,但只要某个东西不再被引用,编译器就会收集它。做这个

{  //start of scope
    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();
}//end of scope

还有其他事情可能会被考虑在内,比如如果你可以创建方法来逻辑地划分事物,在我看来这是一个更好的选择。尽管这样做也没有害处。