我可以指定在等待延续完成之后要保留哪些变量吗
本文关键字:保留 变量 之后 在等待 延续 我可以 | 更新日期: 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
完成后只使用firstName
和city
?
注意:这是一个人为的例子,但如果在下一个线程完成工作时不需要潜在的大数据块,那么能够控制这些数据块的存储似乎是有益的。
不,不能。(除了将它们拆分为单独的方法或将它们设置为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
还有其他事情可能会被考虑在内,比如如果你可以创建方法来逻辑地划分事物,在我看来这是一个更好的选择。尽管这样做也没有害处。