Cualquier diferencia entre "await Task.Run(); return;" y "devolver Task.Run()"? [duplicar]
¿Existe alguna diferencia conceptual entre los siguientes dos fragmentos de código?
async Task TestAsync()
{
await Task.Run(() => DoSomeWork());
}
y
Task TestAsync()
{
return Task.Run(() => DoSomeWork());
}
¿El código generado tampoco difiere?
EDITAR: Para evitar confusiones con Task.Run
un caso similar:
async Task TestAsync()
{
await Task.Delay(1000);
}
y
Task TestAsync()
{
return Task.Delay(1000);
}
ACTUALIZACIÓN ÚLTIMA: Además de la respuesta aceptada, también hay una diferencia en cómo LocalCallContext
se maneja: CallContext.LogicalGetData se restaura incluso cuando no hay asincronía. ¿Por qué?
Una diferencia importante está en la propagación de excepciones. Una excepción, lanzada dentro de un método async Task
, se almacena en el Task
objeto devuelto y permanece inactiva hasta que se observa la tarea mediante await task
, o . Se propaga de esta manera incluso si se lanza desde la parte síncrona del método.task.Wait()
task.Result
task.GetAwaiter().GetResult()
async
Considere el siguiente código, donde OneTestAsync
y AnotherTestAsync
se comportan de manera bastante diferente:
static async Task OneTestAsync(int n)
{
await Task.Delay(n);
}
static Task AnotherTestAsync(int n)
{
return Task.Delay(n);
}
// call DoTestAsync with either OneTestAsync or AnotherTestAsync as whatTest
static void DoTestAsync(Func<int, Task> whatTest, int n)
{
Task task = null;
try
{
// start the task
task = whatTest(n);
// do some other stuff,
// while the task is pending
Console.Write("Press enter to continue");
Console.ReadLine();
task.Wait();
}
catch (Exception ex)
{
Console.Write("Error: " + ex.Message);
}
}
Si llamo DoTestAsync(OneTestAsync, -2)
, produce el siguiente resultado:
Presione enter para continuar Error: se produjeron uno o más errores. Await Task.Delay Error: 2do
Nota, tuve que presionar Enterpara verlo.
Ahora, si llamo DoTestAsync(AnotherTestAsync, -2)
, el flujo de trabajo del código interno DoTestAsync
es bastante diferente, al igual que el resultado. Esta vez, no me pidieron que presionara Enter:
Error: el valor debe ser -1 (lo que significa un tiempo de espera infinito), 0 o un número entero positivo. Nombre del parámetro: milisegundosDelayError: 1.º
En ambos casos Task.Delay(-2)
se lanza al principio, mientras se validan sus parámetros. Este podría ser un escenario inventado, pero en teoría Task.Delay(1000)
también puede ocurrir, por ejemplo, cuando falla la API del temporizador del sistema subyacente.
Como nota al margen, la lógica de propagación de errores es aún diferente para async void
los métodos (a diferencia de async Task
los métodos). Una excepción generada dentro de un async void
método se volverá a lanzar inmediatamente en el contexto de sincronización del hilo actual (a través de SynchronizationContext.Post
), si el hilo actual tiene uno ( SynchronizationContext.Current != null)
. De lo contrario, se volverá a lanzar a través de ThreadPool.QueueUserWorkItem
). La persona que llama no tiene la posibilidad de controlar esta excepción en el mismo marco de pila.
Publiqué más detalles sobre el comportamiento de manejo de excepciones de TPL aquí y aquí .
P : ¿Es posible imitar el comportamiento de propagación de excepciones de async
métodos para métodos no asíncronos Task
, de modo que estos últimos no se arrojen en el mismo marco de pila?
R : Si realmente es necesario, entonces sí, hay un truco para ello:
// async
async Task<int> MethodAsync(int arg)
{
if (arg < 0)
throw new ArgumentException("arg");
// ...
return 42 + arg;
}
// non-async
Task<int> MethodAsync(int arg)
{
var task = new Task<int>(() =>
{
if (arg < 0)
throw new ArgumentException("arg");
// ...
return 42 + arg;
});
task.RunSynchronously(TaskScheduler.Default);
return task;
}
Sin embargo, tenga en cuenta que, bajo ciertas condiciones (como cuando está demasiado profundo en la pila), RunSynchronously
aún podría ejecutarse de forma asincrónica.
Otra diferencia notable es que la versión
async
/ await
es más propensa a bloquearse en un contexto de sincronización no predeterminado . Por ejemplo, lo siguiente se bloqueará en una aplicación WinForms o WPF:
static async Task TestAsync()
{
await Task.Delay(1000);
}
void Form_Load(object sender, EventArgs e)
{
TestAsync().Wait(); // dead-lock here
}
Cámbielo a una versión no asíncrona y no se bloqueará:
Task TestAsync()
{
return Task.Delay(1000);
}
Stephen Cleary explica bien la naturaleza del punto muerto en su blog .
Cuál es la diferencia entre
async Task TestAsync() { await Task.Delay(1000); }
y
Task TestAsync() { return Task.Delay(1000); }
?
Estoy confundido por esta pregunta. Déjame intentar aclarar respondiendo a tu pregunta con otra pregunta. ¿Cuál es la diferencia entre?
Func<int> MakeFunction()
{
Func<int> f = ()=>1;
return ()=>f();
}
y
Func<int> MakeFunction()
{
return ()=>1;
}
?
Cualquiera que sea la diferencia entre mis dos cosas, la misma diferencia hay entre tus dos cosas.
El primer método ni siquiera se compila.
Dado que "
Program.TestAsync()
" es un método asíncrono que devuelve "Task
", una palabra clave de retorno no debe ir seguida de una expresión de objeto. ¿ Tenías intención de regresar 'Task<T>
'?Tiene que ser
async Task TestAsync() { await Task.Run(() => DoSomeWork()); }
Existe una gran diferencia conceptual entre estos dos. El primero es asincrónico, el segundo no. Lea Rendimiento asíncrono: comprensión de los costos de Async y espere para conocer un poco más sobre los aspectos internos de
async
/await
.Generan código diferente.
.method private hidebysig instance class [mscorlib]System.Threading.Tasks.Task TestAsync () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 25 53 4f 54 65 73 74 50 72 6f 6a 65 63 74 2e 50 72 6f 67 72 61 6d 2b 3c 54 65 73 74 41 73 79 6e 63 3e 64 5f 5f 31 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x216c // Code size 62 (0x3e) .maxstack 2 .locals init ( [0] valuetype SOTestProject.Program/'<TestAsync>d__1', [1] class [mscorlib]System.Threading.Tasks.Task, [2] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder ) IL_0000: ldloca.s 0 IL_0002: ldarg.0 IL_0003: stfld class SOTestProject.Program SOTestProject.Program/'<TestAsync>d__1'::'<>4__this' IL_0008: ldloca.s 0 IL_000a: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Create() IL_000f: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder SOTestProject.Program/'<TestAsync>d__1'::'<>t__builder' IL_0014: ldloca.s 0 IL_0016: ldc.i4.m1 IL_0017: stfld int32 SOTestProject.Program/'<TestAsync>d__1'::'<>1__state' IL_001c: ldloca.s 0 IL_001e: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder SOTestProject.Program/'<TestAsync>d__1'::'<>t__builder' IL_0023: stloc.2 IL_0024: ldloca.s 2 IL_0026: ldloca.s 0 IL_0028: call instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Start<valuetype SOTestProject.Program/'<TestAsync>d__1'>(!!0&) IL_002d: ldloca.s 0 IL_002f: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder SOTestProject.Program/'<TestAsync>d__1'::'<>t__builder' IL_0034: call instance class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::get_Task() IL_0039: stloc.1 IL_003a: br.s IL_003c IL_003c: ldloc.1 IL_003d: ret } // end of method Program::TestAsync
y
.method private hidebysig instance class [mscorlib]System.Threading.Tasks.Task TestAsync2 () cil managed { // Method begins at RVA 0x21d8 // Code size 23 (0x17) .maxstack 2 .locals init ( [0] class [mscorlib]System.Threading.Tasks.Task CS$1$0000 ) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldftn instance class [mscorlib]System.Threading.Tasks.Task SOTestProject.Program::'<TestAsync2>b__4'() IL_0008: newobj instance void class [mscorlib]System.Func`1<class [mscorlib]System.Threading.Tasks.Task>::.ctor(object, native int) IL_000d: call class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Threading.Tasks.Task::Run(class [mscorlib]System.Func`1<class [mscorlib]System.Threading.Tasks.Task>) IL_0012: stloc.0 IL_0013: br.s IL_0015 IL_0015: ldloc.0 IL_0016: ret } // end of method Program::TestAsync2