Tengo una interfaz que expone algunos métodos asincrónicos. Más específicamente, tiene métodos definidos que devuelven Task o Task <T>. Estoy usando las palabras clave async / await.
Estoy en el proceso de implementar esta interfaz. Sin embargo, en algunos de estos métodos, esta implementación no tiene nada que esperar. Por esa razón, recibo la advertencia del compilador "Este método asincrónico carece de operadores 'en espera' y se ejecutará sincrónicamente ..."
Entiendo por qué recibo el error, pero me pregunto si debería hacer algo al respecto en este contexto. Se siente mal ignorar las advertencias del compilador.
Sé que puedo solucionarlo esperando en Task.Run, pero eso se siente mal para un método que solo realiza algunas operaciones económicas. También parece que agregará una sobrecarga innecesaria a la ejecución, pero tampoco estoy seguro de si eso ya está allí porque la palabra clave async está presente.
¿Debo simplemente ignorar las advertencias o hay alguna forma de evitar esto que no veo?
fuente
async
?async
palabra clave. Todavía puede devolver unTask
usingTask.FromResult
.Task.FromResult
.Respuestas:
La palabra clave async es simplemente un detalle de implementación de un método; no es parte de la firma del método. Si la implementación o anulación de un método en particular no tiene nada que esperar, simplemente omita la palabra clave async y devuelva una tarea completada usando Task.FromResult <TResult> :
public Task<string> Foo() // public async Task<string> Foo() { // { Baz(); // Baz(); return Task.FromResult("Hello"); // return "Hello"; } // }
Si su método devuelve Task en lugar de Task <TResult> , puede devolver una tarea completada de cualquier tipo y valor.
Task.FromResult(0)
parece ser una opción popular:public Task Bar() // public async Task Bar() { // { Baz(); // Baz(); return Task.FromResult(0); // } // }
O, a partir de .NET Framework 4.6, puede devolver Task.CompletedTask :
public Task Bar() // public async Task Bar() { // { Baz(); // Baz(); return Task.CompletedTask; // } // }
fuente
await Task.FromResult(0)
? ¿Qué talawait Task.Yield()
?async
método, regresa enTask.FromResult(0)
lugar de esperarlo.Es perfectamente razonable que algunas operaciones "asincrónicas" se completen sincrónicamente, pero aún así se ajusten al modelo de llamadas asincrónicas por el polimorfismo.
Un ejemplo real de esto es con las API de E / S del SO. Las llamadas asincrónicas y superpuestas en algunos dispositivos siempre se completan en línea (la escritura en una tubería implementada mediante memoria compartida, por ejemplo). Pero implementan la misma interfaz que las operaciones de varias partes que continúan en segundo plano.
fuente
Michael Liu respondió bien a su pregunta sobre cómo puede evitar la advertencia: devolviendo Task.FromResult.
Voy a responder a la parte de su pregunta "¿Debería preocuparme por la advertencia?".
¡La respuesta es sí!
La razón de esto es que la advertencia aparece con frecuencia cuando llama a un método que regresa
Task
dentro de un método asíncrono sin elawait
operador. Acabo de arreglar un error de concurrencia que sucedió porque invoqué una operación en Entity Framework sin esperar la operación anterior.Si puede escribir meticulosamente su código para evitar las advertencias del compilador, cuando haya una advertencia, se destacará como un pulgar adolorido. Podría haber evitado varias horas de depuración.
fuente
await
dentro del método en un lugar (no habrá CS1998) pero no significa que no habrá otra llamada al método asnyc que carezca de sincronización (usandoawait
o cualquier otro). Ahora, si alguien quisiera saber cómo asegurarse de que no se pierda la sincronización accidentalmente, asegúrese de no ignorar otra advertencia: CS4014. Incluso recomendaría amenazarlo como error.Puede que sea demasiado tarde, pero podría ser útil una investigación:
Hay una estructura interna del código compilado ( IL ):
public static async Task<int> GetTestData() { return 12; }
se convierte en en IL:
.method private hidebysig static class [mscorlib]System.Threading.Tasks.Task`1<int32> GetTestData() cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 28 55 73 61 67 65 4C 69 62 72 61 72 79 2E // ..(UsageLibrary. 53 74 61 72 74 54 79 70 65 2B 3C 47 65 74 54 65 // StartType+<GetTe 73 74 44 61 74 61 3E 64 5F 5F 31 00 00 ) // stData>d__1.. .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) // Code size 52 (0x34) .maxstack 2 .locals init ([0] class UsageLibrary.StartType/'<GetTestData>d__1' V_0, [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> V_1) IL_0000: newobj instance void UsageLibrary.StartType/'<GetTestData>d__1'::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create() IL_000c: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder' IL_0011: ldloc.0 IL_0012: ldc.i4.m1 IL_0013: stfld int32 UsageLibrary.StartType/'<GetTestData>d__1'::'<>1__state' IL_0018: ldloc.0 IL_0019: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder' IL_001e: stloc.1 IL_001f: ldloca.s V_1 IL_0021: ldloca.s V_0 IL_0023: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class UsageLibrary.StartType/'<GetTestData>d__1'>(!!0&) IL_0028: ldloc.0 IL_0029: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder' IL_002e: call instance class [mscorlib]System.Threading.Tasks.Task`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task() IL_0033: ret } // end of method StartType::GetTestData
Y sin método asincrónico y de tarea:
public static int GetTestData() { return 12; }
se convierte en:
.method private hidebysig static int32 GetTestData() cil managed { // Code size 8 (0x8) .maxstack 1 .locals init ([0] int32 V_0) IL_0000: nop IL_0001: ldc.i4.s 12 IL_0003: stloc.0 IL_0004: br.s IL_0006 IL_0006: ldloc.0 IL_0007: ret } // end of method StartType::GetTestData
Como puede ver, la gran diferencia entre estos métodos. Si no usa el método await inside async y no le importa usar el método async (por ejemplo, una llamada a la API o un controlador de eventos), la buena idea lo convertirá al método de sincronización normal (ahorra el rendimiento de su aplicación).
Actualizado:
También hay información adicional de microsoft docs https://docs.microsoft.com/en-us/dotnet/standard/async-in-depth :
fuente
async/await
está muy simplificada, ya que la basa en su ejemplo poco realista de una sola operación que está vinculada a la CPU.Task
s cuando se usa correctamente permite mejorar el rendimiento y la capacidad de respuesta de la aplicación debido a tareas concurrentes (es decir, en paralelo) y una mejor administración y uso de subprocesosTasks
. Es triste que no estés leyendo el texto completo de la publicación y sacando conclusiones rápidamente.int
(como en su caso) y uno que devuelveTask
como lo discutió el OP. Lea su publicación y la respuesta aceptada nuevamente en lugar de tomarse las cosas personalmente. Su respuesta no es útil en este caso. Ni siquiera te molestas en mostrar la diferencia entre un método que tieneawait
dentro o no. Ahora que había hecho que eso habría sido muy bueno bien vale la pena una upvoteNota sobre el comportamiento de excepción al regresar
Task.FromResult
Aquí hay una pequeña demostración que muestra la diferencia en el manejo de excepciones entre los métodos marcados y no marcados con
async
.public Task<string> GetToken1WithoutAsync() => throw new Exception("Ex1!"); // Warning: This async method lacks 'await' operators and will run synchronously. Consider ... public async Task<string> GetToken2WithAsync() => throw new Exception("Ex2!"); public string GetToken3Throws() => throw new Exception("Ex3!"); public async Task<string> GetToken3WithAsync() => await Task.Run(GetToken3Throws); public async Task<string> GetToken4WithAsync() { throw new Exception("Ex4!"); return await Task.FromResult("X");} public static async Task Main(string[] args) { var p = new Program(); try { var task1 = p.GetToken1WithoutAsync(); } catch( Exception ) { Console.WriteLine("Throws before await.");}; var task2 = p.GetToken2WithAsync(); // Does not throw; try { var token2 = await task2; } catch( Exception ) { Console.WriteLine("Throws on await.");}; var task3 = p.GetToken3WithAsync(); // Does not throw; try { var token3 = await task3; } catch( Exception ) { Console.WriteLine("Throws on await.");}; var task4 = p.GetToken4WithAsync(); // Does not throw; try { var token4 = await task4; } catch( Exception ) { Console.WriteLine("Throws on await.");}; }
// .NETCoreApp,Version=v3.0 Throws before await. Throws on await. Throws on await. Throws on await.
(Publicación cruzada de mi respuesta para When async Task <T> required by interface, how to get return variable without compiler warning )
fuente