Mi devo preoccupare “Questo metodo asincrono manca ‘attendono’ operatori e verrà eseguito in modo sincrono” avviso

Ho un interfaccia che espone alcuni metodi asincroni. Più specificamente, ci ha metodi definito il ritorno di Attività o Compito<T>. Io sto usando la async/await parole chiave.

Io sono nel processo di implementazione di questa interfaccia. Tuttavia, in alcuni di questi metodi di questa implementazione non ha nulla da aspettare. Per questo motivo io sono sempre dell’avviso del compilatore “Questo metodo asincrono manca ‘attendono’ operatori e verrà eseguito in modo sincrono…”

Capisco perché io sono sempre l’errore, ma sto chiedendo se devo fare nulla in questo contesto. Ci si sente sbagliato ignorare gli avvisi del compilatore.

So che posso risolvere il problema in attesa di un Compito.Correre, ma che si sente male per un metodo che è solo fare un paio di economici delle operazioni. Suona anche come si aggiunge un overhead non necessario per l’esecuzione, ma poi anch’io non sono sicuro se questo è già lì, perché la async parola chiave è presente.

Devo ignorare gli avvertimenti o c’è un modo per aggirare questo che non sto vedendo?

  • Sta andando a dipendere da specifiche. Sei davvero sicuro che si desidera che queste operazioni possano essere eseguite in modo sincrono? Se si desidera loro di essere eseguite in modo sincrono, perché è il metodo contrassegnato come async?
  • È sufficiente rimuovere il async parola chiave. È ancora possibile restituire un Task utilizzando Task.FromResult.
  • In un esempio davvero non basta assegnare alcuni valori di proprietà, in modo che io sono decisamente a proprio agio con esso l’esecuzione in modo sincrono. Più in particolare su come gestire l’avviso del compilatore che sembra che potrebbe essere solo quello ovvio di rimozione di parola chiave async
  • Google è pieno di informazioni su di esso, nel caso in cui l’OP non la conosce.
  • Non Michael Liu già che suggerimento? Utilizzare Task.FromResult.
  • Che è stato modificato nel suo commento dopo.
  • In generale, si dovrebbe preoccupare di ogni attenzione in quanto potrebbero rivelarsi bug nell’applicazione.
  • Suppongo che l’OP dell’interfaccia Asincrona nel metodo di nomi. Vi consigliamo di rimuovere async da metodi denominati *Async?
  • E ‘ perfettamente bene per un metodo con il suffisso Async per completare in modo sincrono, se non ha un effettivo asincrona lavoro da fare.

 

5 Replies
  1. 93

    Il async parola chiave è semplicemente un dettaglio di implementazione di un metodo, non è parte della firma del metodo. Se c’è un particolare metodo di attuazione o di sostituzione non ha nulla a che attendono, quindi basta omettere il async parola chiave e restituire un compito completato utilizzando Attività.FromResult<T>:

    public Task<string> Foo()               //   public async Task<string> Foo()
    {                                       //   {
        Baz();                              //       Baz();
        return Task.FromResult("Hello");    //       return "Hello";
    }                                       //   }

    Se il metodo restituisce Attività invece di Attività<T>, quindi è possibile restituire un compito completato di qualsiasi tipo e valore. Task.FromResult(0) sembra essere una scelta popolare:

    public Task Bar()                       //   public async Task Bar()
    {                                       //   {
        Baz();                              //       Baz();
        return Task.FromResult(0);          //
    }                                       //   }

    O, come di .NET Framework 4.6, è possibile tornare Attività.CompletedTask:

    public Task Bar()                       //   public async Task Bar()
    {                                       //   {
        Baz();                              //       Baz();
        return Task.CompletedTask;          //
    }                                       //   }
    • Grazie penso che il bit che mi mancava era il concetto di creazione di un progetto che è stato realizzato, piuttosto che tornare a un compito vero e proprio che come dici tu sarebbe come non avere la parola chiave async. Sembra ovvio, ma io proprio non vederlo!
    • Compito potrebbe fare con un membro statico lungo le linee di Attività.Vuoto per questo scopo. L’intenzione sarebbe un po ‘ più chiaro e mi fa male pensare a tutte queste doverose Attività che tornare a zero, che non è mai necessario.
    • await Task.FromResult(0)? Come circa await Task.Yield()?
    • No, nonasync metodo, invio Task.FromResult(0) invece che l’attendono.
    • In realtà NO, async non è solo un dettaglio di implementazione, ci sono molti dettagli circa uno deve essere consapevole :). Uno deve essere consapevole, qual è la parte che viene eseguito in modo sincrono, che parte in modo asincrono, qual è l’attuale contesto di sincronizzazione per la cronaca, i Compiti sono sempre un po ‘ più veloce, in quanto non c’è stato della macchina dietro le tende:).
    • Grazie, non sapevo di ritorno Task.CompletedTask
    • Potete per favore dare un’occhiata alla mia risposta fornita qui e dare i vostri commenti? @MichaelLiu

  2. 9

    È perfettamente ragionevole che alcuni “asincrono” il completamento delle operazioni in modo sincrono, ma sempre conforme al modello chiamata asincrona per il bene di polimorfismo.

    Un esempio reale di questo con il sistema operativo di I/O Api. Asincrona e sovrapposte le chiamate su alcuni dispositivi completa sempre in linea (la scrittura di un tubo implementato l’uso della memoria condivisa, per esempio). Ma che implementano la stessa interfaccia multi-operazioni che continua in background.

  3. 2

    Michael Liu risposto bene alla tua domanda su come si può evitare il messaggio di avviso: con la restituzione di Attività.FromResult.

    Ho intenzione di rispondere “mi devo preoccupare l’avviso” parte della tua domanda.

    La risposta è Sì!

    La ragione di questo è che l’avviso di frequente i risultati quando si chiama un metodo che restituisce Task all’interno di un metodo asincrono senza await operatore. Ho appena risolto un bug di concorrenza che è accaduto perché ho richiamato un’operazione in Entity Framework, senza aspettare l’operazione precedente.

    Se è possibile meticolosamente scrivere il codice per evitare gli avvisi del compilatore, quindi quando c’è un avviso, si leverà in piedi fuori come un pollice dolente. Ho potuto evitare molte ore di debug.

  4. 1

    Ho trovato un trucco per aggirare questo avviso:

    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();
    }

    esistenza di tale attendono parola chiave ci sarà la frode al compilatore di non alzare l’attenzione, anche se sappiamo che non sarà mai chiamato!!! come la condizione è true sempre tornare prima parte del condizionale ternario (?:), e anche questa variabile è un non-utilizzo del var, verrà omesso nelle build di Rilascio. Non so se c’è qualche effetti collaterali con questo approccio.

  5. 0

    Potrebbe essere troppo tardi, ma potrebbe essere utile indagine:

    C’è sulla struttura interna del codice compilato (IL):

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

    diventa 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

    E senza async e metodo di attività:

     public static int GetTestData()
            {
                return 12;
            }

    diventa :

    .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

    Come avete potuto vedere la grande differenza tra questi metodi. Se non si utilizza attendono all’interno metodo asincrono e non di assistenza sull’utilizzo del metodo asincrono (per esempio la chiamata di API o gestore di eventi) la buona idea verrà convertito in un normale metodo di sincronizzazione (salva l’applicazione delle prestazioni).

Lascia un commento