Могу ли я указать, какие переменные я хочу сохранить после завершения ожидания?
В рамках метода 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 локальных переменных, но использую только 2 из них после await
, есть ли какой-либо атрибут, который я могу украсить мои переменные, чтобы указать, что я намерен использовать только firstName
и city
после await
завершается?
Примечание. Это надуманный пример, но, похоже, было бы полезно удержать хранение потенциально больших блоков данных, если они не понадобятся, когда следующий поток завершит работу.
Ответы
Ответ 1
Нет, вы не можете. (За исключением очевидных решений разбиения их на отдельные методы или установки их на null
).
В этом сценарии компилятор не полностью оптимизирован; он может захватывать больше переменных, чем нужно, и может удерживать их дольше, чем необходимо. Вероятно, это будет то, что Microsoft будет оптимизировать в будущем.
Ответ 2
Вы можете запустить 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();
}
}
}
Компилятор генерирует нечто вроде следующего, частично преобразованного в код С#:
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
Ответ 3
Вы можете создавать области с помощью добавленных фигурных скобок в коде, но всякий раз, когда что-то не упоминается, компилятор все равно будет собирать. сделайте это
{ //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
Есть другие вещи, которые можно принять во внимание, например, если вы можете создавать методы для логического разделения вещей, чем, на мой взгляд, это лучший вариант. Хотя нет никакого вреда и в этом.