Saltar al contenido

¿Debo preocuparme por “Este método asíncrono carece de operadores ‘en espera’ y se ejecutará sincrónicamente” advertencia

Agradecemos tu ayuda para difundir nuestros posts acerca de las ciencias de la computación.

Solución:

los asincrónico la palabra clave es simplemente un detalle de implementación de un método; no forma 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 el asincrónico palabra clave y devolver una tarea completada usando Task.FromResult:

public Task Foo()               //    public async Task Foo()
                                       //    
    Baz();                              //        Baz();
    return Task.FromResult("Hello");    //        return "Hello";
                                       //    

Si su método devuelve Task en lugar de Task, luego 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;          //
                                       //    

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.

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

Hay una estructura interna del código compilado (ILLINOIS):

 public static async Task GetTestData()
    
        return 12;
    

se convierte en en IL:

.method private hidebysig static class [mscorlib]System.Threading.Tasks.Task`1 
        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+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/'d__1' V_0,
           [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 V_1)
  IL_0000:  newobj     instance void UsageLibrary.StartType/'d__1'::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::Create()
  IL_000c:  stfld      valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 UsageLibrary.StartType/'d__1'::'<>t__builder'
  IL_0011:  ldloc.0
  IL_0012:  ldc.i4.m1
  IL_0013:  stfld      int32 UsageLibrary.StartType/'d__1'::'<>1__state'
  IL_0018:  ldloc.0
  IL_0019:  ldfld      valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 UsageLibrary.StartType/'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::Startd__1'>(!!0&)
  IL_0028:  ldloc.0
  IL_0029:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 UsageLibrary.StartType/'d__1'::'<>t__builder'
  IL_002e:  call       instance class [mscorlib]System.Threading.Tasks.Task`1 valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::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á en un método de sincronización normal (ahorra el rendimiento de su aplicación).

Actualizado:

También hay información adicional de microsoft docs:

Los métodos asíncronos deben tener una palabra clave de espera en su cuerpo o nunca cederán. Es importante tener esto en cuenta. Si await no se usa en el cuerpo de un método asincrónico, el compilador de C # generará una advertencia, pero el código se compilará y se ejecutará como si fuera un método normal. Tenga en cuenta que esto también sería increíblemente ineficiente, ya que la máquina de estado generada por el compilador de C # para el método asíncrono no lograría nada.

Tienes la posibilidad difundir este enunciado si lograste el éxito.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *