without understanding method ejemplos ejemplo create await async c# .net asynchronous async-await task

c# - understanding - ¿Debería preocuparme por la advertencia de "Este método asincrónico carece de operadores ''en espera'' y se ejecutará sincrónicamente"?



task c# ejemplo (5)

Tengo una interfaz que expone algunos métodos asincrónicos. Más específicamente, tiene métodos definidos que devuelven Tarea o Tarea <T>. Estoy usando las palabras clave async / wait.

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 una tarea. Ejecutar, pero eso se siente mal para un método que solo está haciendo algunas operaciones de bajo costo. 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 asincrónica está presente.

¿Debo ignorar las advertencias o hay alguna forma de solucionar esto que no estoy viendo?


Encontré un truco para evitar esta advertencia:

public async Task<object> test() { //a pseudo code just to disable the warning about lack of await in async code! var xyz = true ? 0 : await Task.FromResult(0); //use a var name that''s not used later //... your code statements as normal, eg: //throw new NotImplementedException(); }

la existencia de esa palabra clave aguarda fraudará al compilador para no generar la advertencia, ¡incluso si sabemos que nunca se llamará! como la condición es true , siempre devuelve la primera parte del condicional ternario (? :), y también como esta var no utiliza var, se omitirá en las versiones de lanzamiento. No estoy seguro de si hay algún efecto secundario con este enfoque.


Es perfectamente razonable que algunas operaciones "asincrónicas" se completen sincrónicamente y, sin embargo, se ajusten al modelo de llamada asincrónica en aras del polimorfismo.

Un ejemplo real de esto es con las API de E / S del sistema operativo. Las llamadas asíncronas y superpuestas en algunos dispositivos siempre se completan en línea (escribiendo en una tubería implementada usando memoria compartida, por ejemplo). Pero implementan la misma interfaz que las operaciones de varias partes que continúan en segundo plano.


La palabra clave asíncrona 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 asíncrona 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 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; // } // }


Michael Liu respondió bien a su pregunta sobre cómo puede evitar la advertencia: devolviendo Task.FromResult.

Voy a responder la parte de su pregunta "¿Debería preocuparme por la advertencia?".

¡La respuesta es sí!

La razón de esto es que la advertencia se produce con frecuencia cuando se llama a un método que devuelve Task dentro de un método asíncrono sin el operador de await . Acabo de corregir un error de concurrencia que ocurrió porque invoqué una operación en Entity Framework sin esperar la operación anterior.

Si puede escribir meticulosamente su código para evitar advertencias del compilador, cuando haya una advertencia, se destacará como un pulgar dolorido. Podría haber evitado varias horas de depuración.


Puede ser demasiado tarde, pero podría ser una investigación útil:

Se trata de la estructura interna del código compilado ( IL ):

public static async Task<int> GetTestData() { return 12; }

se convierte 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 asíncrono y 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 de espera dentro de async y no le importa usar el método async (por ejemplo, llamada de API o controlador de eventos), la buena idea lo convertirá al método de sincronización normal (ahorra el rendimiento de su aplicación).