2016-02-16 17 views
9

Ho letto molto circa async/await, ma ho ancora qualche carenza nella comprensione della seguente situazione.Lottare con asincrona/attendere C#

La mia domanda è, devo applicare i miei metodi "wrapper" come in DoSomething() o come in DoSomethingAsync().

Quindi, cosa c'è di meglio (e perché): utilizzo lo await in metodi wrapper o restituisco direttamente l'attività?

 public static async void Main() 
     { 
      await DoSomething(); 
      await DoSomethingAsync(); 
     } 

     private static Task DoSomething() 
     { 
      return MyLibrary.DoSomethingAsync(); 
     } 

     private static async Task DoSomethingAsync() 
     { 
      await MyLibrary.DoSomethingAsync().ConfigureAwait(false); 
     } 

     public class MyLibrary 
     { 
      public static async Task DoSomethingAsync() 
      { 
       // Here is some I/O 
      } 
     } 
+0

Cercare di evitare l'uso di 'asincrona main' - vedi http://stackoverflow.com/questions/9208921/async- on-main-method-of-console-app per maggiori informazioni. – rhughes

+0

Questa risposta può esserti utile: http://stackoverflow.com/questions/22808475/how-to-force-execution-to-stop-till-asynchronous-function-is-fully-executed – user3340627

+0

Vedi [Qualsiasi differenza tra "Aspetta Task.Run(); return; "e" return Task.Run() "?] (http://stackoverflow.com/q/21033150/1768303). – Noseratio

risposta

4

risposta di Berin è buona, ma non affronta esplicitamente il caso particolare nella sua interrogazione, che è il seguente esempio:

1: Tornando Task direttamente

private static Task DoSomething() 
{ 
    return MyLibrary.DoSomethingAsync(); 
} 

2: In attesa compito e restituendo il risultato

private static async Task DoSomethingAsync() 
{ 
    await MyLibrary.DoSomethingAsync().ConfigureAwait(false); 
} 

In thi caso, l'unica differenza tra la restituzione dello TaskTask e la restituzione della risposta è che - in quest'ultimo caso - una macchina di stato deve essere creata dal framework per gestire l'avvio, la sospensione e la continuazione del metodo in attesa dello Task . Ciò comporta un sovraccarico delle prestazioni.

In generale, se è possibile restituire uno Task e attendere che sia in attesa più in alto, è necessario. Nella maggior parte dei casi reali (ma certamente non tutti), tuttavia, ciò non sarà possibile in quanto si vorrà eseguire qualche elaborazione del risultato prima di tornare (e questo è esattamente ciò che async/await ti aiuta a raggiungere).

+0

Dovresti anche notare che 'async' crea implicitamente il' Task' per te, mentre un metodo regolare deve chiamare 'Task.StartNew()' se vuole restituire un oggetto 'Task' stesso. –

+0

@BerinLoritsch solo se è intenzione di creare effettivamente una nuova attività - in questo caso l'attività è già fornita da 'MyLibrary' e può essere semplicemente restituita. –

+0

Grazie per la risposta. Non dovresti utilizzare Task.FromResult() invece Task.StartNew() quando si restituisce come Attività dal metodo normale? – coalmee

8

Async/Await sono ancora relativamente nuovi, ma esistono alcune buone pratiche per aiutare a chiarire un'API. Le basi sono questo:

  • Metodo dichiarandosi come async significa che si aspetta di await seguito
  • async crea implicitamente un Task per voi.
  • await è come un segnalibro. L'applicazione riprende dove è stata utilizzata la parola chiave await.
  • Non si può await nulla che non sia IAwaitable (più comunemente una Task) (citation)

In un'applicazione in cui ci sono sia le chiamate asincrone e chiamate sincrone, abbiamo adottato una convenzione di denominazione:

  • async chiamate restituire Task o Task<T> e aggiungere la parola Async alla fine del nome.
  • Le chiamate sincrone (impostazione predefinita) funzionano semplicemente come qualsiasi altro metodo e non esistono convenzioni speciali.

Spesso, ci possono essere due metodi che fanno la stessa cosa, ma uno è sincrono e l'altro no. Puoi implementarlo in due modi diversi oppure puoi completarne uno con l'altro. Dipende davvero dalle tue esigenze e da ciò che ti darà l'applicazione più reattiva.

Nell'esempio sopra riportato, il modo corretto per gestire le chiamate di metodo asincrona e normale sarebbe per MyLibrary per esporre due metodi. L'esempio potrebbe essere come questo:

public static class MyLibrary 
{ 
    public static void DoSomething() 
    { 
     // do some work 
    } 

    public static async Task DoSomethingAsync() 
    { 
     // do similar work but use async API, 
     // can also simply call DoSomething(). 
    } 
} 

// In another part of code would be called like this: 
public static async Task BiggerMethod() 
{ 
    MyLibrary.DoSomething(); 
    await MyLibrary.DoSomethingAsync(); 
} 

Che cosa si vuole evitare è avvolgendo un metodo async con un metodo regolare. Non appena lavori con lo Task direttamente, perdi tutti i vantaggi di async e await e introduci luoghi in cui il codice può essere bloccato.

+0

per amor di verità, 'Non puoi aspettare che tutto ciò che non è un compito' è una dichiarazione vaga :) puoi aspettare qualsiasi 'attesa', che non deve essere compito – pkuderov

+0

@pkuderov, grazie. Questo è in realtà qualcosa che non sapevo. –

0

Non c'è differenza in caso di one-liner. Ma in caso di at least two-async-liners, ad esempio:

public static async void Main() 
{ 
    await DoSomething(); 
    await DoSomethingAsync(); 
} 

private static Task DoSomethingTwice() 
{ 
    var do1 = MyLibrary.DoSomethingAsync(); 

    // when you await DoSomethingTwice, next line's reached 
    // when do1 task may not be completed 
    var do2 = MyLibrary.DoSomethingAsync(); 

    // return what??? 
    // how to combine them? 

    // okay, you can do that 
    return Task.WhenAll(do1, do2) 
} 

private static async Task DoSomethingTwiceAsync() 
{ 
    await MyLibrary.DoSomethingAsync().ConfigureAwait(false); 

    // when you await DoSomethingTwiceAsync, next line's reached 
    // when prev-line task is completed 
    await MyLibrary.DoSomethingAsync().ConfigureAwait(false); 
} 

public class MyLibrary 
{ 
    public static async Task DoSomethingAsync() 
    { 
     // Here is some I/O 
    } 
} 

Sommario:

  • se dovete organizzarvi metodo come una catena di sequenziali paci asincroni di lavoro: doAsync1() ->doAsync2() ->doAsync3(),

vale a dire che ogni prossima pace necessita del risultato completo della precedente, quindi è necessario attendere ogni chiamata come:

async Task DoAsync() 
{ 
    await doAsync1(); 
    await doAsync2(); 
    await doAsync3(); 
} 
  • ma non è possibile utilizzare attendono in non-async metodi. Quindi non puoi farlo nello stile Task DoSomething(), solo come async Task DoSomethingAsync().
  • se non c'è nessuna catena asincrono - si può fare quello che vuoi, come nel mio primo esempio di cui sopra
+0

È importante notare che quanto sopra ('DoAsync') è appropriato solo se è necessario eseguire le attività in modo sequenziale: se si desidera che siano concomitanti, è necessario impostarle tutte e * quindi * attendere. –

+0

@AntP Grazie! Questo è esattamente ciò che intendevo come una "catena di chiamate asincrone". Forse dovrei invece dire "pene sequenziali di lavoro sequenziali". E il tuo secondo caso è nel mio primo esempio - metodo 'DoSomethingTwice'. – pkuderov